Side-by-side head row (3:2 flex, wraps when tight): plugin name, version, and GitHub repo as whole-cell links (name/version -> GitHub/npm), with the version/repo baked in from package.json via esbuild define. A once-per-page npm registry check adds an 'Update!' badge + update-command tooltip on the version cell whenever the registry release differs; failures stay silent. Bilingual strings, cell-hover underline + brand border affordance.
101 lines
4.2 KiB
JavaScript
Executable File
101 lines
4.2 KiB
JavaScript
Executable File
#!/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/)')
|