mirror of
https://github.com/sanbuphy/claude-code-source-code.git
synced 2026-04-04 03:54:56 +08:00
- Build script (scripts/build.mjs) using esbuild with iterative stub creation - Stubs for Bun compile-time intrinsics (feature(), MACRO, bun:bundle) - Stub modules for feature-gated internal code paths - QUICKSTART.md with 3 build options (pre-built, esbuild, Bun) - tsconfig.json, package.json for build tooling Build reaches ~95% — 108 remaining feature-gated modules need Bun runtime for full dead code elimination. See QUICKSTART.md for details. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
159 lines
5.6 KiB
JavaScript
159 lines
5.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* stub-modules.mjs — Create stub files for all missing feature-gated modules
|
|
*
|
|
* Run: node scripts/stub-modules.mjs
|
|
* Then: npx esbuild build-src/entry.ts --bundle --platform=node --packages=external ...
|
|
*
|
|
* Reads esbuild errors, resolves each relative import to its correct absolute
|
|
* path inside build-src/src/, and creates an empty stub.
|
|
*/
|
|
|
|
import { readFile, writeFile, mkdir, stat } from 'node:fs/promises'
|
|
import { join, dirname, resolve } from 'node:path'
|
|
import { execSync } from 'node:child_process'
|
|
|
|
const ROOT = join(import.meta.dirname, '..')
|
|
const BUILD_SRC = join(ROOT, 'build-src', 'src')
|
|
|
|
async function exists(p) { try { await stat(p); return true } catch { return false } }
|
|
|
|
// Parse all missing modules from esbuild output
|
|
const out = execSync(
|
|
`npx esbuild "${join(ROOT, 'build-src', 'entry.ts')}" ` +
|
|
`--bundle --platform=node --packages=external ` +
|
|
`--external:'bun:*' --log-level=error --log-limit=0 ` +
|
|
`--outfile=/dev/null 2>&1 || true`,
|
|
{ cwd: ROOT, shell: true, encoding: 'utf8', maxBuffer: 50 * 1024 * 1024 }
|
|
)
|
|
|
|
const missingRe = /Could not resolve "([^"]+)"/g
|
|
const errors = [...out.matchAll(/\s+(\S+:\d+:\d+):\s/g)].map(m => m[1])
|
|
const moduleFiles = new Map() // module → set of importing files
|
|
|
|
let match
|
|
while ((match = missingRe.exec(out)) !== null) {
|
|
const mod = match[1]
|
|
if (mod.startsWith('node:') || mod.startsWith('bun:') || mod.startsWith('/')) continue
|
|
moduleFiles.set(mod, new Set())
|
|
}
|
|
|
|
// Now resolve each relative module path to its absolute path
|
|
// by finding which source file imports it
|
|
const importRe = /(\S+:\d+:\d+):\s*\d+.*require\(["']([^"']+)["']\)|import.*from\s*["']([^"']+)["']/g
|
|
|
|
let stubCount = 0
|
|
const created = new Set()
|
|
|
|
for (const [mod] of moduleFiles) {
|
|
// For relative imports, we need to find the importing file to resolve the path
|
|
// Search for the import in the build-src
|
|
const escapedMod = mod.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
const grepResult = execSync(
|
|
`grep -rl "${escapedMod}" "${BUILD_SRC}" 2>/dev/null || true`,
|
|
{ encoding: 'utf8', maxBuffer: 10 * 1024 * 1024, shell: true }
|
|
).trim()
|
|
|
|
const importers = grepResult.split('\n').filter(Boolean)
|
|
|
|
for (const importer of importers) {
|
|
const importerDir = dirname(importer)
|
|
const absPath = resolve(importerDir, mod)
|
|
|
|
// Check if it's a .d.ts type file — just create empty
|
|
if (mod.endsWith('.d.ts')) {
|
|
if (!created.has(absPath)) {
|
|
await mkdir(dirname(absPath), { recursive: true }).catch(() => {})
|
|
if (!await exists(absPath)) {
|
|
await writeFile(absPath, '// Type stub\nexport {}\n', 'utf8')
|
|
stubCount++
|
|
created.add(absPath)
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Text assets (.txt, .md)
|
|
if (/\.(txt|md)$/.test(mod)) {
|
|
if (!created.has(absPath)) {
|
|
await mkdir(dirname(absPath), { recursive: true }).catch(() => {})
|
|
if (!await exists(absPath)) {
|
|
await writeFile(absPath, '', 'utf8')
|
|
stubCount++
|
|
created.add(absPath)
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
|
|
// JS/TS modules
|
|
if (/\.[tj]sx?$/.test(mod)) {
|
|
if (!created.has(absPath)) {
|
|
await mkdir(dirname(absPath), { recursive: true }).catch(() => {})
|
|
if (!await exists(absPath)) {
|
|
const name = mod.split('/').pop().replace(/\.[tj]sx?$/, '')
|
|
const safeName = name.replace(/[^a-zA-Z0-9_$]/g, '_') || 'stub'
|
|
await writeFile(absPath, `// Auto-generated stub for feature-gated module: ${mod}\nexport default function ${safeName}() { return null }\nexport const ${safeName} = () => null\n`, 'utf8')
|
|
stubCount++
|
|
created.add(absPath)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also try resolving from src root for modules starting with ../
|
|
if (mod.startsWith('../')) {
|
|
// Try from several likely locations
|
|
for (const prefix of ['src', 'src/commands', 'src/components', 'src/services', 'src/tools', 'src/utils']) {
|
|
const absPath = join(ROOT, 'build-src', prefix, mod)
|
|
if (!created.has(absPath)) {
|
|
await mkdir(dirname(absPath), { recursive: true }).catch(() => {})
|
|
if (!await exists(absPath) && (/\.[tj]sx?$/.test(mod))) {
|
|
const name = mod.split('/').pop().replace(/\.[tj]sx?$/, '')
|
|
const safeName = name.replace(/[^a-zA-Z0-9_$]/g, '_') || 'stub'
|
|
await writeFile(absPath, `// Auto-generated stub for: ${mod}\nexport default function ${safeName}() { return null }\nexport const ${safeName} = () => null\n`, 'utf8')
|
|
stubCount++
|
|
created.add(absPath)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`✅ Created ${stubCount} stubs for ${moduleFiles.size} missing modules`)
|
|
|
|
// Now try the build
|
|
console.log('\n🔨 Attempting esbuild bundle...\n')
|
|
try {
|
|
const OUT = join(ROOT, 'dist', 'cli.js')
|
|
await mkdir(dirname(OUT), { recursive: true })
|
|
|
|
execSync([
|
|
'npx esbuild',
|
|
`"${join(ROOT, 'build-src', 'entry.ts')}"`,
|
|
'--bundle',
|
|
'--platform=node',
|
|
'--target=node18',
|
|
'--format=esm',
|
|
`--outfile="${OUT}"`,
|
|
'--packages=external',
|
|
'--external:bun:*',
|
|
'--banner:js=$\'#!/usr/bin/env node\\n// Claude Code v2.1.88 (built from source)\\n// Copyright (c) Anthropic PBC. All rights reserved.\\n\'',
|
|
'--allow-overwrite',
|
|
'--log-level=warning',
|
|
'--sourcemap',
|
|
].join(' '), {
|
|
cwd: ROOT,
|
|
stdio: 'inherit',
|
|
shell: true,
|
|
})
|
|
|
|
const size = (await stat(OUT)).size
|
|
console.log(`\n✅ Build succeeded: ${OUT}`)
|
|
console.log(` Size: ${(size / 1024 / 1024).toFixed(1)}MB`)
|
|
console.log(` Usage: node ${OUT} --version`)
|
|
} catch (e) {
|
|
console.error('\n❌ Build still has errors. Run again to iterate:')
|
|
console.error(' node scripts/stub-modules.mjs')
|
|
}
|