| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- /** Prepare the disposable npm-project view used by an unpackaged Electron shell. */
- import {
- existsSync,
- lstatSync,
- mkdirSync,
- readFileSync,
- readdirSync,
- realpathSync,
- rmSync,
- symlinkSync,
- unlinkSync,
- } from 'node:fs'
- import { dirname, join } from 'node:path'
- import { createDevelopmentProjectMetadata } from '../src/project-manager.ts'
- import type { DesktopRelease } from '../src/release.ts'
- interface PackageManifest {
- readonly name?: string
- readonly version?: string
- }
- /** Inputs whose locations differ between the launcher and isolated tests. */
- export interface DevelopmentProjectOptions {
- /** Directory replaced with the generated development project. */
- readonly projectDir: string
- /** Current workspace's `apps/cli` package directory. */
- readonly cliDir: string
- /** Current workspace's private Desktop Host application directory. */
- readonly hostDir: string
- /** pnpm's workspace-wide virtual-hoist directory. */
- readonly dependencyDir: string
- /** Release identity written into the disposable project metadata. */
- readonly release: DesktopRelease
- }
- function readManifest(path: string): PackageManifest {
- return JSON.parse(readFileSync(path, 'utf8')) as PackageManifest
- }
- function removeOwnedPath(path: string): void {
- let stat: ReturnType<typeof lstatSync>
- try {
- stat = lstatSync(path)
- } catch (error) {
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return
- throw error
- }
- if (stat.isSymbolicLink()) {
- unlinkSync(path)
- return
- }
- if (stat.isDirectory()) {
- rmSync(path, { recursive: true })
- return
- }
- unlinkSync(path)
- }
- function linkDirectory(source: string, destination: string): void {
- mkdirSync(dirname(destination), { recursive: true })
- symlinkSync(realpathSync(source), destination, process.platform === 'win32' ? 'junction' : 'dir')
- }
- function mirrorDependencyLinks(sourceRoot: string, destinationRoot: string): void {
- for (const entry of readdirSync(sourceRoot, { withFileTypes: true })) {
- if (entry.name === '.bin') continue
- const source = join(sourceRoot, entry.name)
- if (entry.name.startsWith('@') && (entry.isDirectory() || entry.isSymbolicLink())) {
- mkdirSync(join(destinationRoot, entry.name), { recursive: true })
- for (const scoped of readdirSync(source, { withFileTypes: true })) {
- if (!scoped.isDirectory() && !scoped.isSymbolicLink()) continue
- linkDirectory(join(source, scoped.name), join(destinationRoot, entry.name, scoped.name))
- }
- continue
- }
- if (entry.isDirectory() || entry.isSymbolicLink()) linkDirectory(source, join(destinationRoot, entry.name))
- }
- }
- /**
- * Replace one disposable project with links to the current built workspace.
- * @param options - Project destination, CLI package, and release identity.
- * @returns the absolute project directory supplied by the caller.
- */
- export function prepareDevelopmentProject(options: DevelopmentProjectOptions): string {
- const cliManifest = readManifest(join(options.cliDir, 'package.json'))
- if (cliManifest.name !== '@deepseek-ai/dsh' || cliManifest.version !== options.release.version) {
- throw new Error(
- `desktop development: apps/cli must be @deepseek-ai/dsh@${options.release.version}, found `
- + `${String(cliManifest.name)}@${String(cliManifest.version)}`,
- )
- }
- if (!existsSync(options.dependencyDir)) {
- throw new Error('desktop development: workspace dependency links are missing; run pnpm install')
- }
- const hostManifest = readManifest(join(options.hostDir, 'package.json'))
- if (hostManifest.name !== '@deepseek-ai/dsh-desktop-host' || hostManifest.version !== options.release.version) {
- throw new Error(
- `desktop development: apps/desktop-host must be @deepseek-ai/dsh-desktop-host@${options.release.version}, found `
- + `${String(hostManifest.name)}@${String(hostManifest.version)}`,
- )
- }
- if (!existsSync(join(options.hostDir, 'lib', 'index.js'))) {
- throw new Error('desktop development: apps/desktop-host/lib/index.js is missing; run pnpm run build')
- }
- removeOwnedPath(options.projectDir)
- createDevelopmentProjectMetadata(options.projectDir, options.release)
- const destinationModules = join(options.projectDir, 'node_modules')
- mkdirSync(destinationModules, { recursive: true })
- mirrorDependencyLinks(options.dependencyDir, destinationModules)
- const dshLink = join(destinationModules, '@deepseek-ai', 'dsh')
- removeOwnedPath(dshLink)
- linkDirectory(options.cliDir, dshLink)
- const hostLink = join(destinationModules, '@deepseek-ai', 'dsh-desktop-host')
- removeOwnedPath(hostLink)
- linkDirectory(options.hostDir, hostLink)
- return options.projectDir
- }
|