build.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { chmodSync, existsSync, mkdirSync } from 'fs'
  2. import { dirname } from 'path'
  3. const pkg = await Bun.file(new URL('../package.json', import.meta.url)).json() as {
  4. name: string
  5. version: string
  6. }
  7. const args = process.argv.slice(2)
  8. const compile = args.includes('--compile')
  9. const dev = args.includes('--dev')
  10. function runCommand(cmd: string[]): string | null {
  11. const proc = Bun.spawnSync({
  12. cmd,
  13. cwd: process.cwd(),
  14. stdout: 'pipe',
  15. stderr: 'pipe',
  16. })
  17. if (proc.exitCode !== 0) {
  18. return null
  19. }
  20. return new TextDecoder().decode(proc.stdout).trim() || null
  21. }
  22. function getDevVersion(baseVersion: string): string {
  23. const timestamp = new Date().toISOString()
  24. const date = timestamp.slice(0, 10).replaceAll('-', '')
  25. const time = timestamp.slice(11, 19).replaceAll(':', '')
  26. const sha = runCommand(['git', 'rev-parse', '--short=8', 'HEAD']) ?? 'unknown'
  27. return `${baseVersion}-dev.${date}.t${time}.sha${sha}`
  28. }
  29. function getVersionChangelog(): string {
  30. return (
  31. runCommand(['git', 'log', '--format=%h %s', '-20']) ??
  32. 'Local development build'
  33. )
  34. }
  35. const defaultFeatures = ['VOICE_MODE']
  36. const featureSet = new Set(defaultFeatures)
  37. for (let i = 0; i < args.length; i += 1) {
  38. const arg = args[i]
  39. if (arg === '--feature' && args[i + 1]) {
  40. featureSet.add(args[i + 1]!)
  41. i += 1
  42. continue
  43. }
  44. if (arg.startsWith('--feature=')) {
  45. featureSet.add(arg.slice('--feature='.length))
  46. }
  47. }
  48. const features = [...featureSet]
  49. const outfile = compile
  50. ? dev
  51. ? './dist/cli-dev'
  52. : './dist/cli'
  53. : dev
  54. ? './cli-dev'
  55. : './cli'
  56. const buildTime = new Date().toISOString()
  57. const version = dev ? getDevVersion(pkg.version) : pkg.version
  58. mkdirSync(dirname(outfile), { recursive: true })
  59. const externals = [
  60. '@ant/*',
  61. 'audio-capture-napi',
  62. 'image-processor-napi',
  63. 'modifiers-napi',
  64. 'url-handler-napi',
  65. ]
  66. const defines = {
  67. 'process.env.USER_TYPE': JSON.stringify('external'),
  68. 'process.env.CLAUDE_CODE_FORCE_FULL_LOGO': JSON.stringify('true'),
  69. ...(dev
  70. ? { 'process.env.NODE_ENV': JSON.stringify('development') }
  71. : {}),
  72. ...(dev
  73. ? {
  74. 'process.env.CLAUDE_CODE_EXPERIMENTAL_BUILD': JSON.stringify('true'),
  75. }
  76. : {}),
  77. 'process.env.CLAUDE_CODE_VERIFY_PLAN': JSON.stringify('false'),
  78. 'MACRO.VERSION': JSON.stringify(version),
  79. 'MACRO.BUILD_TIME': JSON.stringify(buildTime),
  80. 'MACRO.PACKAGE_URL': JSON.stringify(pkg.name),
  81. 'MACRO.NATIVE_PACKAGE_URL': 'undefined',
  82. 'MACRO.FEEDBACK_CHANNEL': JSON.stringify('github'),
  83. 'MACRO.ISSUES_EXPLAINER': JSON.stringify(
  84. 'This reconstructed source snapshot does not include Anthropic internal issue routing.',
  85. ),
  86. 'MACRO.VERSION_CHANGELOG': JSON.stringify(
  87. dev ? getVersionChangelog() : 'https://github.com/paoloanzn/claude-code',
  88. ),
  89. } as const
  90. const cmd = [
  91. 'bun',
  92. 'build',
  93. './src/entrypoints/cli.tsx',
  94. '--compile',
  95. '--target',
  96. 'bun',
  97. '--format',
  98. 'esm',
  99. '--outfile',
  100. outfile,
  101. '--minify',
  102. '--bytecode',
  103. '--packages',
  104. 'bundle',
  105. '--conditions',
  106. 'bun',
  107. ]
  108. for (const external of externals) {
  109. cmd.push('--external', external)
  110. }
  111. for (const feature of features) {
  112. cmd.push(`--feature=${feature}`)
  113. }
  114. for (const [key, value] of Object.entries(defines)) {
  115. cmd.push('--define', `${key}=${value}`)
  116. }
  117. const proc = Bun.spawnSync({
  118. cmd,
  119. cwd: process.cwd(),
  120. stdout: 'inherit',
  121. stderr: 'inherit',
  122. })
  123. if (proc.exitCode !== 0) {
  124. process.exit(proc.exitCode ?? 1)
  125. }
  126. if (existsSync(outfile)) {
  127. chmodSync(outfile, 0o755)
  128. }
  129. console.log(`Built ${outfile}`)