|
| 1 | +#!/usr/bin/env node |
| 2 | +/** |
| 3 | + * Stage the Node runtime + @objectos/app tree under |
| 4 | + * `apps/objectos-desktop/runtime/`, so Tauri's resource bundler can ship it. |
| 5 | + * |
| 6 | + * Reuses the same bits scripts/build-desktop.sh produces, but in-tree |
| 7 | + * (no zipping). Idempotent; re-run after changing @objectos/app. |
| 8 | + * |
| 9 | + * runtime/ |
| 10 | + * node | node.exe |
| 11 | + * app/ |
| 12 | + * desktop.mjs |
| 13 | + * package.json |
| 14 | + * dist/objectstack.json |
| 15 | + * node_modules/ |
| 16 | + */ |
| 17 | +import { execSync } from 'node:child_process'; |
| 18 | +import { existsSync, mkdirSync, copyFileSync, rmSync, chmodSync } from 'node:fs'; |
| 19 | +import { dirname, join, resolve } from 'node:path'; |
| 20 | +import { fileURLToPath } from 'node:url'; |
| 21 | +import { platform, arch } from 'node:os'; |
| 22 | + |
| 23 | +const HERE = dirname(fileURLToPath(import.meta.url)); |
| 24 | +const PKG = resolve(HERE, '..'); |
| 25 | +const REPO = resolve(PKG, '../..'); |
| 26 | +const APP = resolve(REPO, 'apps/objectos'); |
| 27 | +const RUNTIME = resolve(PKG, 'runtime'); |
| 28 | +const NODE_VERSION = process.env.NODE_VERSION ?? '20.18.1'; |
| 29 | + |
| 30 | +const osName = ({ darwin: 'darwin', linux: 'linux', win32: 'win' })[platform()]; |
| 31 | +const archName = ({ x64: 'x64', arm64: 'arm64' })[arch()]; |
| 32 | +if (!osName || !archName) { |
| 33 | + console.error(`unsupported platform: ${platform()}/${arch()}`); |
| 34 | + process.exit(1); |
| 35 | +} |
| 36 | + |
| 37 | +const sh = (cmd, opts = {}) => { |
| 38 | + console.log(`$ ${cmd}`); |
| 39 | + execSync(cmd, { stdio: 'inherit', ...opts }); |
| 40 | +}; |
| 41 | + |
| 42 | +if (!existsSync(join(APP, 'dist/objectstack.json'))) { |
| 43 | + sh('pnpm --filter @objectos/app build', { cwd: REPO }); |
| 44 | +} |
| 45 | + |
| 46 | +rmSync(RUNTIME, { recursive: true, force: true }); |
| 47 | +mkdirSync(join(RUNTIME, 'app/dist'), { recursive: true }); |
| 48 | +copyFileSync(join(APP, 'package.json'), join(RUNTIME, 'app/package.json')); |
| 49 | +copyFileSync(join(APP, 'desktop.mjs'), join(RUNTIME, 'app/desktop.mjs')); |
| 50 | +copyFileSync(join(APP, 'dist/objectstack.json'), join(RUNTIME, 'app/dist/objectstack.json')); |
| 51 | + |
| 52 | +sh( |
| 53 | + 'npm install --omit=dev --no-audit --no-fund --loglevel=error --legacy-peer-deps better-sqlite3@^12.9.0', |
| 54 | + { cwd: join(RUNTIME, 'app') } |
| 55 | +); |
| 56 | + |
| 57 | +const cache = resolve(REPO, '.cache/node'); |
| 58 | +mkdirSync(cache, { recursive: true }); |
| 59 | +const ext = osName === 'win' ? 'zip' : osName === 'linux' ? 'tar.xz' : 'tar.gz'; |
| 60 | +const pkgName = `node-v${NODE_VERSION}-${osName === 'win' ? 'win' : osName}-${archName}.${ext}`; |
| 61 | +const tarball = join(cache, pkgName); |
| 62 | +if (!existsSync(tarball)) { |
| 63 | + sh(`curl -fSL --retry 3 -o "${tarball}" https://nodejs.org/dist/v${NODE_VERSION}/${pkgName}`); |
| 64 | +} |
| 65 | +const extractDir = join(cache, pkgName.replace(/\.(tar\.(gz|xz)|zip)$/, '')); |
| 66 | +if (!existsSync(extractDir)) { |
| 67 | + if (ext === 'tar.gz') sh(`tar -xzf "${tarball}" -C "${cache}"`); |
| 68 | + else if (ext === 'tar.xz') sh(`tar -xJf "${tarball}" -C "${cache}"`); |
| 69 | + else sh(`cd "${cache}" && unzip -q "${tarball}"`); |
| 70 | +} |
| 71 | +if (osName === 'win') { |
| 72 | + copyFileSync(join(extractDir, 'node.exe'), join(RUNTIME, 'node.exe')); |
| 73 | +} else { |
| 74 | + const dst = join(RUNTIME, 'node'); |
| 75 | + copyFileSync(join(extractDir, 'bin/node'), dst); |
| 76 | + chmodSync(dst, 0o755); |
| 77 | +} |
| 78 | + |
| 79 | +console.log(`✓ staged runtime → ${RUNTIME}`); |
0 commit comments