mirror of
https://github.com/codeaashu/claude-code.git
synced 2026-04-08 22:28:48 +03:00
166 lines
5.1 KiB
Markdown
166 lines
5.1 KiB
Markdown
# Prompt 03: Create esbuild-Based Build System
|
|
|
|
## Context
|
|
|
|
You are working in `/workspaces/claude-code`. This is the Claude Code CLI — a TypeScript/TSX terminal app using React + Ink. It was originally built using **Bun's bundler** with feature flags, but that build config wasn't included in the leak.
|
|
|
|
We need to create a build system that:
|
|
1. Bundles the entire `src/` tree into a runnable output
|
|
2. Aliases `bun:bundle` → our shim at `src/shims/bun-bundle.ts`
|
|
3. Injects the `MACRO` global (via `src/shims/macro.ts` preload)
|
|
4. Handles TSX/JSX (React)
|
|
5. Handles ESM `.js` extension imports (the code uses `import from './foo.js'` which maps to `./foo.ts`)
|
|
6. Produces output that can run under **Bun** (primary) or **Node.js 20+** (secondary)
|
|
|
|
## Existing Files
|
|
|
|
- `src/shims/bun-bundle.ts` — runtime `feature()` function (created in Prompt 02)
|
|
- `src/shims/macro.ts` — global `MACRO` object (created in Prompt 02)
|
|
- `src/shims/preload.ts` — preload bootstrap (created in Prompt 02)
|
|
- `src/entrypoints/cli.tsx` — main entrypoint
|
|
- `tsconfig.json` — has `"jsx": "react-jsx"`, `"module": "ESNext"`, `"moduleResolution": "bundler"`
|
|
|
|
## Task
|
|
|
|
### Part A: Install esbuild
|
|
|
|
```bash
|
|
bun add -d esbuild
|
|
```
|
|
|
|
### Part B: Create build script
|
|
|
|
Create `scripts/build-bundle.ts` (a Bun-runnable build script):
|
|
|
|
```ts
|
|
// scripts/build-bundle.ts
|
|
// Usage: bun scripts/build-bundle.ts [--watch] [--minify]
|
|
|
|
import * as esbuild from 'esbuild'
|
|
import { resolve } from 'path'
|
|
|
|
const ROOT = resolve(import.meta.dir, '..')
|
|
const watch = process.argv.includes('--watch')
|
|
const minify = process.argv.includes('--minify')
|
|
|
|
const buildOptions: esbuild.BuildOptions = {
|
|
entryPoints: [resolve(ROOT, 'src/entrypoints/cli.tsx')],
|
|
bundle: true,
|
|
platform: 'node',
|
|
target: 'node20',
|
|
format: 'esm',
|
|
outdir: resolve(ROOT, 'dist'),
|
|
outExtension: { '.js': '.mjs' },
|
|
|
|
// Inject the MACRO global before all other code
|
|
inject: [resolve(ROOT, 'src/shims/macro.ts')],
|
|
|
|
// Alias bun:bundle to our runtime shim
|
|
alias: {
|
|
'bun:bundle': resolve(ROOT, 'src/shims/bun-bundle.ts'),
|
|
},
|
|
|
|
// Don't bundle node built-ins or native packages
|
|
external: [
|
|
// Node built-ins
|
|
'fs', 'path', 'os', 'crypto', 'child_process', 'http', 'https',
|
|
'net', 'tls', 'url', 'util', 'stream', 'events', 'buffer',
|
|
'querystring', 'readline', 'zlib', 'assert', 'tty', 'worker_threads',
|
|
'perf_hooks', 'async_hooks', 'dns', 'dgram', 'cluster',
|
|
'node:*',
|
|
// Native addons that can't be bundled
|
|
'fsevents',
|
|
],
|
|
|
|
jsx: 'automatic',
|
|
|
|
// Source maps for debugging
|
|
sourcemap: true,
|
|
|
|
minify,
|
|
|
|
// Banner: shebang for CLI + preload the MACRO global
|
|
banner: {
|
|
js: '#!/usr/bin/env node\n',
|
|
},
|
|
|
|
// Handle the .js → .ts resolution that the codebase uses
|
|
resolveExtensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
|
|
|
|
logLevel: 'info',
|
|
}
|
|
|
|
async function main() {
|
|
if (watch) {
|
|
const ctx = await esbuild.context(buildOptions)
|
|
await ctx.watch()
|
|
console.log('Watching for changes...')
|
|
} else {
|
|
const result = await esbuild.build(buildOptions)
|
|
if (result.errors.length > 0) {
|
|
console.error('Build failed')
|
|
process.exit(1)
|
|
}
|
|
console.log('Build complete → dist/')
|
|
}
|
|
}
|
|
|
|
main().catch(err => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|
|
```
|
|
|
|
**Important**: This is a starting point. You will likely need to iterate on the externals list and alias configuration. The codebase has ~1,900 files — some imports may need special handling. When you run the build:
|
|
|
|
1. Run it: `bun scripts/build-bundle.ts`
|
|
2. Look at the errors
|
|
3. Fix them (add externals, fix aliases, etc.)
|
|
4. Repeat until it bundles successfully
|
|
|
|
Common issues you'll hit:
|
|
- **npm packages that use native modules** → add to `external`
|
|
- **Dynamic `require()` calls** behind `process.env.USER_TYPE === 'ant'` → these are Anthropic-internal, wrap them or stub them
|
|
- **Circular dependencies** → esbuild handles these but may warn
|
|
- **Re-exports from barrel files** → should work but watch for issues
|
|
|
|
### Part C: Add npm scripts
|
|
|
|
Add these to `package.json` `"scripts"`:
|
|
|
|
```json
|
|
{
|
|
"build": "bun scripts/build-bundle.ts",
|
|
"build:watch": "bun scripts/build-bundle.ts --watch",
|
|
"build:prod": "bun scripts/build-bundle.ts --minify"
|
|
}
|
|
```
|
|
|
|
### Part D: Create dist output directory
|
|
|
|
Add `dist/` to `.gitignore` (create one if it doesn't exist).
|
|
|
|
### Part E: Iterate on build errors
|
|
|
|
Run the build and fix whatever comes up. The goal is a clean `bun scripts/build-bundle.ts` that produces `dist/cli.mjs`.
|
|
|
|
**Strategy for unresolvable modules**: If modules reference Anthropic-internal packages or Bun-specific APIs (like `Bun.hash`, `Bun.file`), create minimal stubs in `src/shims/` that provide compatible fallbacks.
|
|
|
|
### Part F: Test the output
|
|
|
|
After a successful build:
|
|
```bash
|
|
node dist/cli.mjs --version
|
|
# or
|
|
bun dist/cli.mjs --version
|
|
```
|
|
|
|
This should print the version. It will likely crash after that because no API key is configured — that's fine for now.
|
|
|
|
## Verification
|
|
|
|
1. `bun scripts/build-bundle.ts` completes without errors
|
|
2. `dist/cli.mjs` exists
|
|
3. `bun dist/cli.mjs --version` or `node dist/cli.mjs --version` prints a version string
|
|
4. `package.json` has `build`, `build:watch`, `build:prod` scripts
|