Files
deepseek-harness/scripts/compile-referenced-projects.ts
T

112 lines
4.9 KiB
TypeScript

/** Compile and bundle package projects without repeating aggregate test and script typechecks. */
import { spawnSync } from 'node:child_process'
import { relative, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { parseArgs } from 'node:util'
import ts from 'typescript'
import { pnpmCommand } from './release/process.ts'
type CompilationMode = 'checked' | 'benchmark-emit'
/**
* Run one package-manager command serially with the inherited build environment.
* @param root - Repository root supplying the command's working directory.
* @param args - Package-manager arguments, without shell quoting.
* @throws If the command cannot start or exits unsuccessfully.
*/
export function runPnpmCommand(root: string, args: readonly string[]): void {
const [command, ...prefix] = pnpmCommand()
const result = spawnSync(command, [...prefix, ...args], { cwd: root, stdio: 'inherit' })
if (result.error !== undefined) throw result.error
if (result.status !== 0) {
throw new Error(`compile-referenced-projects: pnpm ${args.join(' ')} exited with ${String(result.status ?? result.signal)}`)
}
}
/**
* Compile one aggregate's project references with the serial TypeScript builder.
* @param root - Repository root containing the aggregate tsconfig.
* @param face - Independent compiler face whose package projects are emitted.
* @param mode - Check package types by default; benchmark emission defers diagnostics to required full builds.
* @throws If the aggregate emits files, its configuration is invalid, or package compilation fails.
*/
export function compileReferencedProjects(
root: string,
face: 'host' | 'client',
mode: CompilationMode = 'checked',
): void {
const configPath = resolve(root, `tsconfig.${face}.json`)
const config = ts.getParsedCommandLineOfConfigFile(configPath, {}, {
...ts.sys,
onUnRecoverableConfigFileDiagnostic(diagnostic) {
throw new Error(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'))
},
})
if (config === undefined) throw new Error(`compile-referenced-projects: cannot read ${configPath}`)
const references = config.projectReferences
if (references === undefined || references.length === 0) {
throw new Error(`compile-referenced-projects: ${configPath} requires non-empty project references`)
}
if (config.errors.length > 0) {
throw new Error(config.errors
.map(diagnostic => ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'))
.join('\n'))
}
if (config.options.noEmit !== true) {
throw new Error(`compile-referenced-projects: ${configPath} requires noEmit: true to exclude its aggregate program`)
}
const compiler = fileURLToPath(import.meta.resolve('typescript/bin/tsc'))
const result = spawnSync(process.execPath, [
...face === 'host' ? ['--max-old-space-size=4096'] : [],
compiler,
'-b',
...mode === 'benchmark-emit' ? ['--noCheck'] : [],
// Relative arguments keep the complete workspace graph below Windows' command-line limit.
...references.map(reference => relative(root, reference.path)),
], { cwd: root, stdio: 'inherit' })
if (result.error !== undefined) throw result.error
if (result.status !== 0) {
throw new Error(`compile-referenced-projects: ${face} compiler exited with ${String(result.status ?? result.signal)}`)
}
}
/**
* Emit package libraries in Host, Desktop, then Client dependency order.
* @param root - Repository root containing the compiler and bundle configurations.
* @param hostOnly - Omit the Client compiler and bundler after the Host artifacts are ready.
* @param mode - Check package types by default; benchmark emission defers diagnostics to required full builds.
* @throws If any compiler or bundler fails.
*/
export function buildLibraryArtifacts(root: string, hostOnly: boolean, mode: CompilationMode = 'checked'): void {
compileReferencedProjects(root, 'host', mode)
runPnpmCommand(root, ['exec', 'tsdown', '--config-loader', 'native', '--env.DSH_BUILD_FACE', 'host'])
runPnpmCommand(root, ['--filter', '@deepseek-ai/dsh-desktop', 'run', 'bundle'])
if (hostOnly) return
compileReferencedProjects(root, 'client', mode)
runPnpmCommand(root, ['exec', 'tsdown', '--config-loader', 'native', '--env.DSH_BUILD_FACE', 'client'])
}
if (import.meta.main) {
const { positionals } = parseArgs({ allowPositionals: true })
const [mode] = positionals
if (positionals.length !== 1) {
throw new Error('compile-referenced-projects: expected exactly one mode: host, client, host-libraries, or libraries')
}
const root = resolve(import.meta.dirname, '..')
switch (mode) {
case 'host':
case 'client':
compileReferencedProjects(root, mode)
break
case 'host-libraries':
buildLibraryArtifacts(root, true)
break
case 'libraries':
buildLibraryArtifacts(root, false)
break
default:
throw new Error('compile-referenced-projects: expected exactly one mode: host, client, host-libraries, or libraries')
}
}