#!/usr/bin/env node /** * dsh-context build — one runtime dependency (zod, for the projection's wire * schema; every `@deepseek-ai/*` contract is type-only and erased here). * * Produces the npm-package artifacts under lib/: * lib/index.js — host half: src/host/ bundled to plain ESM (zod external). * lib/client.js — client half: src/client/ bundled 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`, so * `react` is marked external and stays a bare require). * * Both halves bundle their local modules (shared types are type-only and * vanish at build time). 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: bundled TS -> ESM (zod stays external — the user's harness // provides the wire schema validator, like every other @deepseek-ai/* runtime // service; the package ships zod as a regular dependency) ---------------- await build({ entryPoints: [join(ROOT, 'src', 'host', 'index.ts')], outfile: join(ROOT, 'lib', 'index.js'), format: 'esm', platform: 'node', target: 'es2022', bundle: true, external: ['zod'], sourcemap: false, }) // ---- client half: bundled TS -> CJS, then wrap in the loader handoff ------- // `react` stays external (the browser module table answers the injected // require), as do the framework seed words this bundle uses — esbuild turns // JSX into plain React.createElement calls (classic transform), and the loader // resolves the shared framework singletons at runtime. Everything else is // inlined by the bundler. await build({ entryPoints: [join(ROOT, 'src', 'client', 'index.ts')], outfile: join(ROOT, 'lib', '_client.js'), format: 'cjs', platform: 'browser', target: 'es2020', bundle: true, external: ['react', '@deepseek-ai/dsh-client-ui-primitives'], // Plugin self-metadata (shown on the Plugin info card), read from // package.json so the card always matches the shipped release. define: { __DSH_CTX_VERSION__: JSON.stringify(pkg.version), __DSH_CTX_REPO__: JSON.stringify( String((pkg.repository && pkg.repository.url) || '').replace(/^git\+/, '').replace(/\.git$/, ''), ), }, 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/ */', '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('sessionProjections')) { throw new Error('host half must inject the sessionProjections service') } console.log(`built ${pkg.name}:`) console.log(' lib/index.js (host half, ESM, from src/host/)') console.log(' lib/client.js (client half, __ModuleLoader__ closure bundle, from src/client/)')