Embedding
Plume is a Swift package that any application can embed. The host application provides templates, context values, functions, components and resource handling; Plume parses, checks and renders.
This page covers the embedding APIs, from a one-line render to a full host that emits resources and the client runtime.
Rendering a template
The core API is PlumeTemplate:
import Plume
let template = try PlumeTemplate("""
<h1>{site.title}</h1>
""")
let html = try template.render([
"site": ["title": "My Site"]
])Use render when you only need HTML.
Naming sources
Use sourceName when you have one. Diagnostics, editor tooling, scoped resources and host-side file resolution are easier to understand when Plume knows which file is being rendered:
let template = try PlumeTemplate(
source,
sourceName: "theme/pages/home.plume"
)Collecting resources
Templates can declare styles, scripts, images, state and navigation. Use renderResult when the host needs those collected declarations alongside the HTML:
let result = try template.renderResult(context)
print(result.html)
print(result.styles)
print(result.scripts)
print(result.state)
print(result.navigation)
print(result.requiresRuntime)The host decides how resources are emitted. A host typically turns collected styles and scripts into fingerprinted files, emits responsive images and injects runtime scripts only when needed.
Use renderResult for any template that may contain @style, @script, @image, @state, event bindings or @navigation. See Resources for the resource declarations themselves.
Components
Pass component sources into the template environment:
let environment = try PlumeTemplateEnvironment(componentSources: [
"components/PostCard.plume": postCardSource,
"components/PageSection.plume": pageSectionSource
])
let template = try PlumeTemplate(
source,
sourceName: "home.plume",
environment: environment
)Components defined inside the current template are collected automatically.
Safe HTML
Ordinary strings are escaped. Use PlumeSafeHTML for trusted HTML that the host has already rendered or sanitised:
let html = try template.render([
"content": PlumeSafeHTML("<p>Rendered Markdown</p>")
])The template interpolates it like any other value:
<article>{content}</article>Host functions
Host applications can expose functions:
let html = try template.render([
"asset": PlumeFunction { call in
guard let path = call.arguments.first as? String else {
return ""
}
return "/assets/" + path
}
])Templates call the function like any other expression:
<link rel="stylesheet" href="{asset('site.css')}">Use functions for host-specific behaviour such as asset resolution, URL generation, image helpers or formatting values that should remain outside the template language.
Diagnostics
Use PlumeLanguageSupport for editor-facing tooling:
let diagnostics = PlumeLanguageSupport.diagnostics(
for: source,
sourceName: "theme/home.plume",
componentSources: components
)The standalone language server and editor extensions use the same language support APIs.
Checking many files
When checking many files that share components, build the environment once and reuse it:
let environment = PlumeLanguageSupport.environment(componentSources: components)
for file in files {
let diagnostics = PlumeLanguageSupport.diagnostics(
for: file.source,
sourceName: file.name,
environment: environment
)
}This parses each component once instead of once per checked file.
Emitting the runtime
Emit the Plume runtime only when result.requiresRuntime is true. Static templates, components, styles, scripts, assets and images do not need it unless the page also uses state, event bindings, browser actions or navigation.
Plume ships the runtime and the scoped-style rewriter as public API, so hosts do not need to implement the client-side contract themselves:
let runtime = PlumeBrowserRuntime.javaScript
for style in result.styles {
var css = style.css ?? ""
if let attribute = style.scopeAttribute {
css = PlumeCSSScoper.scope(css, attribute: attribute)
}
// emit css
}Write PlumeBrowserRuntime.javaScript to a file, or inline it, on pages that require the runtime. Use PlumeCSSScoper.scope to apply a scoped style's scope attribute to its CSS before emitting it.
Navigation
When @navigation is enabled, the runtime intercepts same-origin links, fetches the next page, swaps the configured root element, updates the document title, loads missing Plume styles and module scripts, uses View Transitions where available and falls back to a normal page load on errors.
Navigation works best when the configured root exists on every participating page. Downloads, external sites, anchors, feeds and custom protocols should keep their normal browser behaviour.
Host shape
A small host usually needs these pieces:
- A loader for template source files.
- A loader for component source files.
- A context builder that turns application data into dictionaries, arrays, strings, numbers, booleans and
PlumeSafeHTML. - Host functions such as
asset(). - A resource emitter for collected styles, scripts, images and runtime files.
Start with render() while prototyping. Move to renderResult() when templates begin declaring resources or interactivity.
Responsibilities
Plume renders templates and records resource declarations. A host application is responsible for:
- Loading templates and component sources.
- Supplying context data.
- Marking trusted HTML as
PlumeSafeHTML. - Providing functions such as
asset(). - Resolving
@style(file:),@script(file:)and@imagereferences. - Emitting collected styles and scripts.
- Emitting the runtime when
requiresRuntimeis true.