Skip to content

Kody is live

Watch the launch video — what Kody is, and why it exists.

← Public packages

@kentcdodds/package-app-kit

Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.

starter-remix/app/assets/entry.ts

50 lines · 1.6 KB · TypeScript
import { run } from 'remix/ui'
import { Counter } from '../ui/counter.tsx'
import { InstallCta } from '../ui/install-cta.tsx'
import { UpdateBanner } from '../ui/update-banner.tsx'
import { DoubleCheckButton } from '../ui/double-check-button.tsx'
import { AboutPanel } from '../ui/about-panel.tsx'
import { NotesDemo } from '../ui/notes-demo.tsx'

// One browser module — hydration resolves islands by export name (no import map).
const clientEntries: Record<string, unknown> = {
	Counter,
	InstallCta,
	UpdateBanner,
	DoubleCheckButton,
	AboutPanel,
	NotesDemo,
}

const app = run({
	async loadModule(_moduleUrl, exportName) {
		const component = clientEntries[exportName]
		if (typeof component !== 'function') {
			throw new Error(`Unknown client entry "${exportName}"`)
		}
		return component
	},
})

app.addEventListener('error', (event) => {
	console.error('Hydration error:', event.error)
})

function registerServiceWorker() {
	if (!('serviceWorker' in navigator)) return
	const root = document.documentElement
	const appBase = (root.getAttribute('data-app-base') || '').replace(/\/$/, '')
	const assetBase = (root.getAttribute('data-asset-base') || '').replace(/\/$/, '')
	const swUrl =
		root.getAttribute('data-sw-url') ||
		(assetBase ? `${assetBase}/sw.js` : `${appBase}/sw.js`)
	if (!appBase || !swUrl) return
	void navigator.serviceWorker
		.register(swUrl, { scope: `${appBase}/`, updateViaCache: 'none' })
		.catch((error) => console.warn('SW register failed', error))
}

void app.ready().then(() => {
	document.documentElement.dataset.hydrated = 'true'
	registerServiceWorker()
})