← Public packages
@kentcdodds/package-app-kit
Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.
scripts/sync-client-assets.mjs
120 lines · 4.2 KB · JavaScript#!/usr/bin/env node
/**
* Interim fallback only: bundle src/client/index.ts → single ESM string in
* src/client-assets.ts so older platforms (no clientModuleUrl) can still serve
* `/client/boot.js` from the Worker.
*
* Authoring source of truth is TypeScript under src/client/ (kody.app.client).
* Prefer packageContext.clientModuleUrl when non-null.
*
* Run: node scripts/sync-client-assets.mjs
*/
import { writeFileSync } from 'node:fs'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import { spawnSync } from 'node:child_process'
const root = join(dirname(fileURLToPath(import.meta.url)), '..')
const entry = join(root, 'src/client/index.ts')
async function loadEsbuild() {
try {
return await import('esbuild')
} catch {
const require = createRequire(import.meta.url)
try {
return require('esbuild')
} catch {
console.log('esbuild not installed locally; fetching via npx…')
const r = spawnSync(
'npx',
['--yes', 'esbuild@0.28.2', entry, '--bundle', '--format=esm', '--platform=browser', '--target=es2022', '--external:remix/ui', `--outfile=${join(root, '.tmp-boot.js')}`],
{ cwd: root, encoding: 'utf8' },
)
if (r.status !== 0) {
console.error(r.stdout, r.stderr)
process.exit(r.status || 1)
}
const { readFileSync, unlinkSync } = await import('node:fs')
const boot = readFileSync(join(root, '.tmp-boot.js'), 'utf8')
try {
unlinkSync(join(root, '.tmp-boot.js'))
} catch {
/* ignore */
}
return { __boot: boot }
}
}
}
const esbuild = await loadEsbuild()
let boot
if (esbuild.__boot) {
boot = esbuild.__boot
} else {
const result = await esbuild.build({
entryPoints: [entry],
bundle: true,
write: false,
format: 'esm',
platform: 'browser',
target: 'es2022',
external: ['remix/ui'],
logLevel: 'warning',
})
boot = result.outputFiles?.[0]?.text
}
if (!boot) {
console.error('esbuild produced no output')
process.exit(1)
}
const lines = []
lines.push('/**')
lines.push(' * Interim Worker serve helper for `/client/boot.js` + `/app.js`.')
lines.push(' * Generated from `src/client/index.ts` via `node scripts/sync-client-assets.mjs`.')
lines.push(' * Primary path after Cole #2284: `packageContext.clientModuleUrl` (do not import')
lines.push(' * `src/client/index.ts` from the Worker graph).')
lines.push(' */')
lines.push('')
lines.push('const CLIENT_MODULES: Record<string, string> = {')
lines.push(`\t${JSON.stringify('boot.js')}: ${JSON.stringify(boot)},`)
lines.push('}')
lines.push('')
lines.push("export const CLIENT_BOOT_PATH = '/client/boot.js'")
lines.push('')
lines.push('export function listClientModules(): string[] {')
lines.push('\treturn Object.keys(CLIENT_MODULES)')
lines.push('}')
lines.push('')
lines.push('/** Return browser ESM source for a client filename, or null. */')
lines.push('export function readClientModule(filename: string): string | null {')
lines.push("\tconst key = filename.replace(/^\\.\\//, '').replace(/^client\\//, '')")
lines.push('\tif (!Object.prototype.hasOwnProperty.call(CLIENT_MODULES, key)) return null')
lines.push('\treturn CLIENT_MODULES[key]')
lines.push('}')
lines.push('')
lines.push('/** Response for a committed client ESM module. */')
lines.push('export function clientModuleResponse(filename: string): Response | null {')
lines.push('\tconst body = readClientModule(filename)')
lines.push('\tif (body == null) return null')
lines.push('\treturn new Response(body, {')
lines.push('\t\theaders: {')
lines.push("\t\t\t'content-type': 'text/javascript; charset=utf-8',")
lines.push("\t\t\t'cache-control': 'no-store',")
lines.push('\t\t},')
lines.push('\t})')
lines.push('}')
lines.push('')
lines.push('/** `/app.js` and `/client/boot.js` → bundled boot entry (interim). */')
lines.push('export function clientBootResponse(): Response {')
lines.push("\treturn clientModuleResponse('boot.js')!")
lines.push('}')
lines.push('')
lines.push('/** Primary callable: serve boot module Response. */')
lines.push('export default clientBootResponse')
lines.push('')
writeFileSync(join(root, 'src/client-assets.ts'), lines.join('\n'))
console.log('synced interim boot bundle → src/client-assets.ts (' + boot.length + ' bytes)')