Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ node_modules
/.sass-cache
/connect.lock
coverage
coverage-json/
/libpeerconnection.log
npm-debug.log
yarn-error.log
Expand Down Expand Up @@ -73,4 +74,4 @@ generated/

.claude/worktrees
.claude/settings.local.json
.nx/polygraph
.nx/polygraph
4 changes: 0 additions & 4 deletions apps/loom-example-app/src/app-config.ts

This file was deleted.

32 changes: 0 additions & 32 deletions apps/loom-example-app/src/document.ts

This file was deleted.

8 changes: 0 additions & 8 deletions apps/loom-example-app/src/entry-browser.ts

This file was deleted.

24 changes: 11 additions & 13 deletions apps/loom-example-app/src/entry-client.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,40 @@
import "./app.css"
import { Html, mount } from "@effectify/loom"
import { LoomVite } from "@effectify/loom-vite"
import { appBuildId, appPayloadElementId, appRootId } from "./app-config.js"
import { prepareRouteRuntime } from "./router-runtime.js"
import { bodyForResult, resolveAppRequest, titleForResult, todoRoutePath } from "./router.js"
import { bodyForResult, prepareAppRequest, resolveAppRequest, titleForResult, todoRoutePath } from "./router.js"
import * as counterRouteModule from "./routes/counter-route.js"
import * as todoRouteModule from "./routes/todo-route.js"

export const bootstrapClient = (
document: Document,
options?: LoomVite.LoomBootstrapOptions,
): Promise<LoomVite.LoomBootstrapResult> =>
LoomVite.bootstrap(document, {
...options,
expectedBuildId: options?.expectedBuildId ?? appBuildId,
payloadElementId: options?.payloadElementId ?? appPayloadElementId,
})
): Promise<LoomVite.LoomBootstrapResult> => LoomVite.bootstrap(document, options)

const defaultClientUrl = "https://effectify.dev/"

const mountClientRoute = (pathname: string, root: HTMLElement): boolean => {
if (pathname === "/") {
mount({ counterRoute: counterRouteModule.component }, { root })
mount({ counterRoute: counterRouteModule.default }, { root })
return true
}

if (pathname === todoRoutePath) {
mount({ todoRoute: todoRouteModule.component }, { root })
mount({ todoRoute: todoRouteModule.default }, { root })
return true
}

return false
}

const renderClientFallback = async (document: Document): Promise<boolean> => {
const root = document.getElementById(appRootId)
const root = document.getElementById(LoomVite.defaultLoomRootId)

if (!(root instanceof HTMLElement) || root.innerHTML.trim() !== "") {
return false
}

const requestUrl = new URL(document.location?.href ?? defaultClientUrl, defaultClientUrl)
await prepareRouteRuntime(requestUrl)
await prepareAppRequest(requestUrl)
const result = resolveAppRequest(requestUrl)

if (requestUrl.pathname === "/" || requestUrl.pathname === todoRoutePath) {
Expand Down Expand Up @@ -71,3 +65,7 @@ export const startClientApp = async (

return result
}

if (typeof window !== "undefined" && typeof document !== "undefined") {
void startClientApp(document)
}
54 changes: 7 additions & 47 deletions apps/loom-example-app/src/entry-server.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,22 @@
import { LoomNitro } from "@effectify/loom-nitro"
import { Resumability } from "@effectify/loom"
import { appBuildId, appPayloadElementId, appRootId } from "./app-config.js"
import { prepareRouteRuntime } from "./router-runtime.js"
import { bodyForResult, resolveAppRequest, statusForResult, titleForResult } from "./router.js"
import { createDocument } from "./document.js"
import { bodyForResult, prepareAppRequest, resolveAppRequest, statusForResult, titleForResult } from "./router.js"

const applicationBaseUrl = "https://effectify.dev"

const normalizeRequestUrl = (input: string): URL => new URL(input, applicationBaseUrl)

const payloadPlaceholder = `<script type="application/json" id="${appPayloadElementId}"></script>`

const injectResumabilityPayload = (
html: string,
payload: LoomNitro.LoomResumabilityPayload | undefined,
): string => {
if (payload === undefined) {
return html
}

const encodedPayload = Resumability.encodeContract(payload)
.replace(/</g, "\\u003c")
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029")

return html.replace(
payloadPlaceholder,
`<script type="application/json" id="${appPayloadElementId}">${encodedPayload}</script>`,
)
}

export const createServerRenderer = (): LoomNitro.LoomNitroRenderer => {
const renderer = LoomNitro.renderer({
buildId: appBuildId,
rootId: appRootId,
export const createServerRenderer = (): LoomNitro.LoomNitroRenderer =>
LoomNitro.renderer({
render: (request) => {
const requestUrl = normalizeRequestUrl(request.url)

return prepareRouteRuntime(requestUrl).then(() => {
return prepareAppRequest(requestUrl).then(() => {
const result = resolveAppRequest(requestUrl)

return createDocument({
title: titleForResult(result),
return {
title: `Loom Example App · ${titleForResult(result)}`,
body: bodyForResult(result),
})
}
})
},
response: (request) => {
Expand All @@ -54,16 +27,3 @@ export const createServerRenderer = (): LoomNitro.LoomNitroRenderer => {
}
},
})

return {
name: renderer.name,
render: async (request) => {
const result = await renderer.render(request)

return {
...result,
html: injectResumabilityPayload(result.html, result.resumability),
}
},
}
}
Loading
Loading