Files
dsh-context/scripts/build.mjs
T
bowenliang123 0e81996a41 feat: port both halves to strict TypeScript; bump to 0.4.0
- src/host.ts: typed fold model (Snapshot/RequestRecord/SurfaceNode/
  ContextEventRecord), local drift-free service contracts for
  connection/sessions/sessionQuery via declare module augmentation
- src/client.cts: typed client bundle (React types via @types/react,
  locale/slots/connection contracts, typed component props); CJS-flavored
  so esbuild keeps require/module.exports despite type:module
- tsconfig.json: strict, noEmit, verbatimModuleSyntax (runtime-free of
  imports in the client bundle is enforced by the type system)
- scripts/build.mjs: esbuild transpile for both halves, loader wrap and
  smoke checks unchanged; npm run typecheck added to the test suite
- note: @deepseek-ai/* service type packages publish broken dependency
  chains on npm (dsh-paths 404), so local contracts replace them
2026-08-14 19:03:16 +08:00

82 lines
3.1 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* dsh-context build — zero runtime dependencies.
*
* Produces the npm-package artifacts under lib/:
* lib/index.js — host half: src/host.ts transpiled to plain ESM.
* lib/client.js — client half: src/client.ts transpiled to CJS, then
* wrapped in the web boot handoff
* `window.__ModuleLoader__.load({ id, factory })`, the
* closure-factory format every `dsh.client` package's
* `./client` export must use (React and other platform
* modules arrive through the injected `require`).
*
* Type checking is a separate step (`npm run typecheck`, tsc --noEmit);
* this script only transpiles (esbuild) and runs smoke checks: both outputs
* must parse, and the host half must import with the expected plugin shape.
*/
import { build } from 'esbuild'
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
const pkg = JSON.parse(await readFile(join(ROOT, 'package.json'), 'utf8'))
await mkdir(join(ROOT, 'lib'), { recursive: true })
// ---- host half: TS -> ESM (transpile only; the loader imports the entry) ----
await build({
entryPoints: [join(ROOT, 'src', 'host.ts')],
outfile: join(ROOT, 'lib', 'index.js'),
format: 'esm',
platform: 'node',
target: 'es2022',
bundle: false,
sourcemap: false,
})
// ---- client half: TS (.cts) -> CJS, then wrap in the loader handoff ----
// The file is authored as CommonJS TypeScript (`.cts`) so esbuild keeps its
// `require`/`module.exports` semantics despite the package's "type": "module".
await build({
entryPoints: [join(ROOT, 'src', 'client.cts')],
outfile: join(ROOT, 'lib', '_client.js'),
format: 'cjs',
platform: 'browser',
target: 'es2020',
bundle: false,
sourcemap: false,
})
const clientSource = await readFile(join(ROOT, 'lib', '_client.js'), 'utf8')
await rm(join(ROOT, 'lib', '_client.js'))
const bundle = [
'/* dsh-context client bundle — generated by scripts/build.mjs from src/client.ts */',
'window.__ModuleLoader__.load({',
` id: ${JSON.stringify(pkg.name)},`,
' factory: (require) => {',
' var module = { exports: {} };',
' var exports = module.exports;',
clientSource.replace(/\s+$/, '\n'),
' return module.exports;',
' },',
'});',
'',
].join('\n')
await writeFile(join(ROOT, 'lib', 'client.js'), bundle)
// ---- smoke checks ----
new Function(bundle) // syntax parse only; never executes (window is undefined here)
const host = await import(join(ROOT, 'lib', 'index.js'))
if (host.name !== 'dsh-context' || !Array.isArray(host.inject) || typeof host.apply !== 'function') {
throw new Error('host half does not expose the expected plugin shape (name/inject/apply)')
}
if (!host.inject.includes('connection')) {
throw new Error('host half must inject the connection service')
}
console.log(`built ${pkg.name}:`)
console.log(' lib/index.js (host half, ESM, from src/host.ts)')
console.log(' lib/client.js (client half, __ModuleLoader__ closure bundle, from src/client.ts)')