index.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import { z } from "zod"
  2. import { Global } from "../global"
  3. import { Log } from "../util/log"
  4. import path from "path"
  5. import { NamedError } from "../util/error"
  6. import { readableStreamToText } from "bun"
  7. export namespace BunProc {
  8. const log = Log.create({ service: "bun" })
  9. export async function run(
  10. cmd: string[],
  11. options?: Bun.SpawnOptions.OptionsObject<any, any, any>,
  12. ) {
  13. log.info("running", {
  14. cmd: [which(), ...cmd],
  15. ...options,
  16. })
  17. const result = Bun.spawn([which(), ...cmd], {
  18. ...options,
  19. stdout: "pipe",
  20. stderr: "pipe",
  21. env: {
  22. ...process.env,
  23. ...options?.env,
  24. BUN_BE_BUN: "1",
  25. },
  26. })
  27. const code = await result.exited;
  28. const stdout = result.stdout ? typeof result.stdout === "number" ? result.stdout : await readableStreamToText(result.stdout) : undefined
  29. const stderr = result.stderr ? typeof result.stderr === "number" ? result.stderr : await readableStreamToText(result.stderr) : undefined
  30. log.info("done", {
  31. code,
  32. stdout,
  33. stderr,
  34. })
  35. if (code !== 0) {
  36. throw new Error(`Command failed with exit code ${result.exitCode}`)
  37. }
  38. return result
  39. }
  40. export function which() {
  41. return process.execPath
  42. }
  43. export const InstallFailedError = NamedError.create(
  44. "BunInstallFailedError",
  45. z.object({
  46. pkg: z.string(),
  47. version: z.string(),
  48. }),
  49. )
  50. export async function install(pkg: string, version = "latest") {
  51. const mod = path.join(Global.Path.cache, "node_modules", pkg)
  52. const pkgjson = Bun.file(path.join(Global.Path.cache, "package.json"))
  53. const parsed = await pkgjson.json().catch(() => ({
  54. dependencies: {},
  55. }))
  56. if (parsed.dependencies[pkg] === version) return mod
  57. parsed.dependencies[pkg] = version
  58. await Bun.write(pkgjson, JSON.stringify(parsed, null, 2))
  59. await BunProc.run(["install", "--registry=https://registry.npmjs.org"], {
  60. cwd: Global.Path.cache,
  61. }).catch((e) => {
  62. throw new InstallFailedError(
  63. { pkg, version },
  64. {
  65. cause: e,
  66. },
  67. )
  68. })
  69. return mod
  70. }
  71. }