Skip to content

Commit

Permalink
add fragment cache (to match turbo cache)
Browse files Browse the repository at this point in the history
  • Loading branch information
tchak committed Jan 17, 2025
1 parent 64be8a0 commit 70320cb
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
28 changes: 28 additions & 0 deletions packages/react/src/root.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,34 @@ describe('@coldwired/react', () => {
root.destroy();
});

it('render fragment with component and cache', async () => {
document.body.innerHTML = `<${DEFAULT_TAG_NAME}><${REACT_COMPONENT_TAG} ${NAME_ATTRIBUTE}="Counter"></${REACT_COMPONENT_TAG}></${DEFAULT_TAG_NAME}>`;
const root = createRoot({
loader: (name) => Promise.resolve(manifest[name]),
cache: true,
});
await root.render(document.body).done;

expect(document.body.innerHTML).toEqual(
`<${DEFAULT_TAG_NAME} data-fragment-id="fragment-0"><div><p>Count: 0</p><button>Increment</button></div></${DEFAULT_TAG_NAME}><div id="react-root"></div>`,
);

root.destroy();

{
const root = createRoot({
loader: (name) => Promise.resolve(manifest[name]),
cache: true,
});
await root.render(document.body).done;

expect(document.body.innerHTML).toEqual(
`<${DEFAULT_TAG_NAME} data-fragment-id="fragment-0"><div><p>Count: 0</p><button>Increment</button></div></${DEFAULT_TAG_NAME}><div id="react-root"></div>`,
);
root.destroy();
}
});

it('render with error boundary', async () => {
document.body.innerHTML = `<${DEFAULT_TAG_NAME}>
<${REACT_COMPONENT_TAG} ${NAME_ATTRIBUTE}="Counter"></${REACT_COMPONENT_TAG}>
Expand Down
54 changes: 54 additions & 0 deletions packages/react/src/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface RootOptions {
schema?: Partial<Schema>;
layoutComponentName?: string;
errorBoundaryFallbackComponentName?: string;
cache?: boolean;
}

export interface Schema extends TreeBuilderSchema {
Expand Down Expand Up @@ -115,6 +116,11 @@ export function createRoot(
}
await preload(fragment, (names) => manifestLoader(names, loader, manifest), schema);
const tree = hydrate(fragment, manifest, schema);

if (options.cache) {
saveFragmentCache(element, fragmentOrHTML);
}

if (reset) {
element.innerHTML = '';
}
Expand Down Expand Up @@ -145,6 +151,9 @@ export function createRoot(
await mounting;
mounted.set(element, (fragmentOrHTML) => render(element, fragmentOrHTML, false));
registerDestroyer(element);
if (options.cache) {
restoreFragmentCache(element);
}
await render(element, element, true);
}
};
Expand Down Expand Up @@ -278,3 +287,48 @@ async function getErrorBoundaryFallbackComponent(
}
return;
}

let fragmentCacheIdSequence = 0;
const fragmentCacheIdAttributeName = 'data-fragment-id';
const fragmentCache = new Map<string, string>();

export function resetFragmentCache() {
fragmentCache.clear();
}

function saveFragmentCache(element: Element, fragment: DocumentFragmentLike | string) {
let fragmentCacheId = element.getAttribute(fragmentCacheIdAttributeName);
if (!fragmentCacheId) {
fragmentCacheId = `fragment-${fragmentCacheIdSequence++}`;
element.setAttribute(fragmentCacheIdAttributeName, fragmentCacheId);
}
fragmentCache.set(fragmentCacheId, stringifyFragment(fragment));
}

function restoreFragmentCache(element: Element) {
const fragmentCacheId = element.getAttribute(fragmentCacheIdAttributeName);
if (fragmentCacheId) {
const html = fragmentCache.get(fragmentCacheId);
if (html) {
element.innerHTML = html;
}
}
}

function stringifyFragment(fragmentOrHTML: DocumentFragmentLike | string): string {
if (typeof fragmentOrHTML == 'string') {
return fragmentOrHTML;
}
if (isElement(fragmentOrHTML)) {
return fragmentOrHTML.innerHTML;
}
const html: string[] = [];
for (const node of fragmentOrHTML.childNodes) {
if (isElement(node)) {
html.push(node.outerHTML);
} else if (node.nodeType == Node.TEXT_NODE && node.textContent) {
html.push(node.textContent);
}
}
return html.join(' ');
}

0 comments on commit 70320cb

Please sign in to comment.