Search indexes
@jxsuite/search
is the reference implementation of the
emit
capability
— the hook that lets a section-owner class write derived artifacts (here: a search index) into the build output. It is deliberately headless: the extension contributes the
search
project section, the build-time index, a
Search
state class, and a browser client, but no UI. Sites author their own search box against the client's contract.
If you want to add search to a site rather than study the extension, start with the user-level Site search page.
What the manifest declares
{
"name": "@jxsuite/search",
"classes": {
"SearchIndex": "./src/SearchIndex.class.json",
"Search": "./src/Search.class.json"
},
"schemas": { "project": "./schemas/project.fragment.schema.json" }
}
Two classes, one schema fragment:
SearchIndexowns thesearchsection (project.key: "search") and carries two capabilities:projectData(normalizes the section into_project.search) andemit(builds the index).Searchis a state class pages query results through; it compiles away vialower.The fragment contributes the
searchproperty to the composed project schema : anengine(an enum ofminisearchtoday, room for more), anoutputpath, and per-collection index settings.
The
emit
capability
emit
runs at build time, after routes and components compile (extensions.md §8.4). The host passes the section value and a context carrying the
already-loaded
project sections — the emitter never re-reads source files:
emit(sectionValue, { projectConfig, root, sections, routes })
→ [{ path: "/search-index.json", content: "…json…" }]
SearchIndex.emit
walks each configured collection in
sections.content
and produces two document granularities per entry:
a page document — title and description from frontmatter, full text extracted from the entry's rendered
$children;section documents (when
sections: true) — one per heading up tosectionDepth, each with the heading text, the section's own text, and a URL ending in#<heading-id>. Heading ids are assigned by the parser and always match the rendered anchors ( parser.md §3.2 ).
The result is one JSON envelope at the configured
output
path:
{
"version": 1,
"engine": "minisearch",
"fields": ["title", "heading", "text"],
"boost": { "title": 4, "heading": 2 },
"documents": [
{ "id": "docs:framework/site", "url": "/docs/framework/site/", "heading": "", "text": "…" },
{
"id": "docs:framework/site#assets",
"url": "/docs/framework/site/#assets",
"heading": "Assets",
"text": "…"
}
]
}
The emitter returns data;
the host writes the files
, guards against path traversal, and skips the emitter entirely when the project declares no
search
section — the same gating as section loading and server mounts.
Lowering
Search
to client code
A page declares reactive results with the
Search
prototype:
"state": {
"q": "",
"results": { "$prototype": "Search", "query": { "$ref": "#/state/q" }, "timing": "client" }
}
In compiled sites
Search.lower()
replaces that def with a core
Function
computed that lazy-imports the bundled client, preloads the index once, and re-queries whenever
state.q
(or the ready flag) changes. No extension code ships to the browser except the client bundle itself, which the lowered def names via
$bundle
(extensions.md §8.3):
{
"$bundle": ["npm:@jxsuite/search/client"],
"$prototype": "Function",
"timing": "client",
"body": "…import('/assets/jxsuite-search-client.js') … m.query(state.q, {…})…"
}
The compiler's sidecar bundler resolves
npm:@jxsuite/search/client
from
node_modules
, bundles it (MiniSearch inlined) into
/assets/jxsuite-search-client.js
, and strips
$bundle
from the def. The deterministic URL comes from
sidecarAssetPath
in
@jxsuite/schema/asset-paths
, shared by extension and compiler so neither depends on the other.
Inside compiled
components
, use the client's
$src
surface instead — components are not lowered; see
Site search
.
Under node
Search.resolve()
degrades to
[]
with a warning outside the browser — search is a client interaction, and compiler-timing bakes or dev-server resolution should never fail a build over it.
The
emit
contract generalizes beyond search: RSS/Atom feeds, export manifests, or any derived artifact a section owner can compute from loaded project data fits the same shape.