solid-html-cachable
v0.0.2
Published
Patched Solid HTML package with portable normalized built-template cache hooks.
Maintainers
Readme
solid-html-cachable
Patched Solid HTML package that exposes a cacheable built-template boundary validated by the solid-html-cache cache-boundary experiments. The current source snapshot is based on @solidjs/[email protected]; that upstream base is provenance, not a requirement that applications pin every Solid runtime package to rc.13.
Status
Pre-1.0 package. The serialized cache format is explicitly versioned and compiler-fingerprinted; incompatible engine revisions are cache misses, not migrations.
Upstream base:
- package:
@solidjs/[email protected] - Solid commit:
5efaf260becb32293f2bcb4d32f8be72be6de674 - license: MIT
Runtime peer compatibility is declared as a range (@solidjs/web >=2.0.0-rc.13 <3.0.0). The dev/test matrix remains pinned to a known Solid revision for reproducibility.
The normal tagged-template API is preserved:
import html from "solid-html-cachable";
const view = html`<div class="card">Hello ${name}</div>`;html.define(...), html.jsx, and html.components continue to work like upstream.
What this package changes
Upstream @solidjs/html keeps parse(), buildTemplate(), the built root, and instantiation private inside createHtml(). This package adds two things:
createHtml({ resolveRoot })— an optional external root-cache hook.html.compiler— low-level compile / normalize / rehydrate / instantiate operations.
No storage policy is built into this package. A cache library decides keys, localStorage/IndexedDB usage, eviction, and invalidation.
Why normalized built templates
A raw built root contains live HTMLTemplateElement instances owned by the current browser document, so it cannot cross a page refresh directly.
This package normalizes the built root into:
post-build AST
+
template.innerHTML for every document-owned HTMLTemplateElementOn a later document it recreates fresh <template> elements with innerHTML and reattaches them to the post-build AST. That preserves build-time mutations (claim, static-prop filtering, normalized text) while avoiding a second buildTemplate() call.
The research suite proved restoration for static DOM, reactive values, spread ordering, component boundaries, and multiple top-level templates, with zero buildTemplate() calls during restore.
API
createHtml(options?)
import { createHtml } from "solid-html-cachable";
const memory = new Map<string, unknown>();
const html = createHtml({
resolveRoot(strings, compiler) {
const key = JSON.stringify(Array.from(strings));
const memoryHit = memory.get(key);
if (memoryHit) return memoryHit as any;
const serialized = localStorage.getItem(`solid-html:${key}`);
if (serialized) {
const root = compiler.deserialize(serialized);
memory.set(key, root);
return root;
}
const root = compiler.compile(strings);
localStorage.setItem(`solid-html:${key}`, compiler.serialize(root));
memory.set(key, root);
return root;
}
});The normal tagged-template invocation still owns the current expression values:
const view = html`<div>${() => signal()}</div>`;The cache contains no old expression closures, component instances, or reactive owners.
html.compiler
const root = html.compiler.compile(strings);
const portable = html.compiler.toPortable(root);
const serialized = html.compiler.serialize(root);
// after refresh / in a new document
const restored = html.compiler.deserialize(serialized);
const view = html.compiler.instantiate(restored, values, components);The intended integration for solid-html-cache is the resolveRoot hook, not direct calls to instantiate().
Portable format
Every payload includes a format version and exact upstream engine identity. This is intentional cache invalidation metadata, not an npm dependency lock:
{
format: 1,
engine: {
package: "@solidjs/html",
version: "2.0.0-rc.13",
commit: "5efaf260becb32293f2bcb4d32f8be72be6de674"
},
root: { /* post-build AST, no DOM objects */ },
templates: [
{ path: [0], html: "<div class=\"card\">...</div>" }
]
}deserialize() rejects a payload from another format or engine version. See docs/CACHE_FORMAT.md.
Build and publish the package
bun install
bun run build
bunx playwright install chromium
bun run test
npm pack --dry-run
npm publishFiles relevant to an upstream proposal
UPSTREAM_PROPOSAL.md— rationale and suggested upstream scope.docs/UPSTREAM_DELTA.md— exact conceptual changes relative to upstream.patches/upstream-minimal.patch— proposal-oriented patch sketch against the pinned rc.13 implementation.docs/CACHE_FORMAT.md— portable representation and invalidation rules.docs/SOLID_HTML_CACHE_INTEGRATION_PLAN.md— plan for supporting this package and stock@solidjs/htmlfromsolid-html-cache.
Important limitations
- Each published
solid-html-cachablebuild is based on a specific upstream Solid HTML revision, and compiled payloads are intentionally fingerprinted to that build. The runtime peer range can be broader than that upstream source snapshot. - The format is synchronous because tagged-template rendering is synchronous. localStorage is the simplest synchronous backend; IndexedDB needs prewarming or an async preparation phase.
- Rehydration uses
template.innerHTML. Persist only templates produced by your application/compiler cache; do not treat arbitrary untrusted payloads as templates. - This package does not claim server-side or hydration compatibility beyond the tested client DOM path.
Maintaining this fork sustainably
The preferred long-term workflow is upstream source + a small patch, not a
manually diverging copy and not a wrapper that tries to import private symbols
from the published @solidjs/html bundle. The published package does not expose
parse, buildTemplate, the built root, or instantiation as public subpaths, so
a wrapper cannot simply re-export those private closure-local operations.
For each upstream update:
- fetch the chosen Solid source tag/commit;
- copy/build
packages/htmlfrom that revision; - apply the small cache-hook patch (
createHtml({ resolveRoot }), compiler normalize/rehydrate/instantiate API); - update the upstream provenance/fingerprint;
- run the cross-document correctness and refresh benchmarks;
- publish a new
solid-html-cachableversion.
This keeps the maintained delta small and makes an eventual upstream merge straightforward.
