Skip to content

Commit a6bef45

Browse files
fix: 修复 rg 文件的传入
1 parent 7e888ce commit a6bef45

File tree

4 files changed

+212
-2
lines changed

4 files changed

+212
-2
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ coverage
77
.idea
88
.vscode
99
*.suo
10-
*.lock
10+
*.lock
11+
src/utils/vendor/

build.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,19 @@ for (const file of files) {
5353
console.log(
5454
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for Node.js compat)`,
5555
);
56+
57+
// Step 4: Bundle download-ripgrep script as standalone JS for postinstall
58+
const rgScript = await Bun.build({
59+
entrypoints: ["scripts/download-ripgrep.ts"],
60+
outdir,
61+
target: "node",
62+
});
63+
if (!rgScript.success) {
64+
console.error("Failed to bundle download-ripgrep script:");
65+
for (const log of rgScript.logs) {
66+
console.error(log);
67+
}
68+
// Non-fatal — postinstall fallback to bun run scripts/download-ripgrep.ts
69+
} else {
70+
console.log(`Bundled download-ripgrep script to ${outdir}/`);
71+
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"packages/@ant/*"
3333
],
3434
"files": [
35-
"dist"
35+
"dist",
36+
"scripts/download-ripgrep.ts"
3637
],
3738
"scripts": {
3839
"build": "bun run build.ts",
@@ -46,6 +47,7 @@
4647
"test": "bun test",
4748
"check:unused": "knip-bun",
4849
"health": "bun run scripts/health-check.ts",
50+
"postinstall": "node dist/download-ripgrep.js || bun run scripts/download-ripgrep.ts || true",
4951
"docs:dev": "npx mintlify dev"
5052
},
5153
"dependencies": {},

scripts/download-ripgrep.ts

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/**
2+
* Download ripgrep binary from GitHub releases.
3+
*
4+
* Run automatically via `bun install` (postinstall hook),
5+
* or manually: `bun run scripts/download-ripgrep.ts [--force]`
6+
*
7+
* Idempotent — skips download if the binary already exists.
8+
* Use --force to re-download.
9+
*/
10+
11+
import { existsSync, mkdirSync, renameSync, rmSync, statSync } from 'fs'
12+
import { chmodSync } from 'fs'
13+
import { spawnSync } from 'child_process'
14+
import * as path from 'path'
15+
import { fileURLToPath } from 'url'
16+
17+
const __filename = fileURLToPath(import.meta.url)
18+
const __dirname = path.dirname(__filename)
19+
20+
const RG_VERSION = '15.0.1'
21+
const BASE_URL = `https://github.com/microsoft/ripgrep-prebuilt/releases/download/v${RG_VERSION}`
22+
23+
// --- Platform mapping ---
24+
25+
type PlatformMapping = {
26+
target: string
27+
ext: 'tar.gz' | 'zip'
28+
}
29+
30+
function getPlatformMapping(): PlatformMapping {
31+
const arch = process.arch
32+
const platform = process.platform
33+
34+
if (platform === 'darwin') {
35+
if (arch === 'arm64') return { target: 'aarch64-apple-darwin', ext: 'tar.gz' }
36+
if (arch === 'x64') return { target: 'x86_64-apple-darwin', ext: 'tar.gz' }
37+
throw new Error(`Unsupported macOS arch: ${arch}`)
38+
}
39+
40+
if (platform === 'win32') {
41+
if (arch === 'x64') return { target: 'x86_64-pc-windows-msvc', ext: 'zip' }
42+
if (arch === 'arm64') return { target: 'aarch64-pc-windows-msvc', ext: 'zip' }
43+
throw new Error(`Unsupported Windows arch: ${arch}`)
44+
}
45+
46+
if (platform === 'linux') {
47+
const isMusl = detectMusl()
48+
if (arch === 'x64') {
49+
// x64 Linux always uses musl (statically linked, most portable)
50+
return { target: 'x86_64-unknown-linux-musl', ext: 'tar.gz' }
51+
}
52+
if (arch === 'arm64') {
53+
return isMusl
54+
? { target: 'aarch64-unknown-linux-musl', ext: 'tar.gz' }
55+
: { target: 'aarch64-unknown-linux-gnu', ext: 'tar.gz' }
56+
}
57+
throw new Error(`Unsupported Linux arch: ${arch}`)
58+
}
59+
60+
throw new Error(`Unsupported platform: ${platform}`)
61+
}
62+
63+
function detectMusl(): boolean {
64+
const muslArch = process.arch === 'x64' ? 'x86_64' : 'aarch64'
65+
try {
66+
statSync(`/lib/libc.musl-${muslArch}.so.1`)
67+
return true
68+
} catch {
69+
return false
70+
}
71+
}
72+
73+
// --- Target vendor path (must match ripgrep.ts logic) ---
74+
75+
function getVendorDir(): string {
76+
const packageRoot = path.resolve(__dirname, '..')
77+
78+
// Dev mode: package root has src/ directory
79+
// ripgrep.ts at src/utils/ripgrep.ts: __dirname = src/utils/
80+
// vendor path = src/utils/vendor/ripgrep/
81+
if (existsSync(path.join(packageRoot, 'src'))) {
82+
return path.resolve(packageRoot, 'src', 'utils', 'vendor', 'ripgrep')
83+
}
84+
85+
// Published mode: compiled chunks are flat in dist/
86+
// ripgrep chunk at dist/xxxx.js: __dirname = dist/
87+
// vendor path = dist/vendor/ripgrep/
88+
return path.resolve(packageRoot, 'dist', 'vendor', 'ripgrep')
89+
}
90+
91+
function getBinaryPath(): string {
92+
const dir = getVendorDir()
93+
const subdir = `${process.arch}-${process.platform}`
94+
const binary = process.platform === 'win32' ? 'rg.exe' : 'rg'
95+
return path.resolve(dir, subdir, binary)
96+
}
97+
98+
// --- Download & extract ---
99+
100+
async function downloadAndExtract(): Promise<void> {
101+
const { target, ext } = getPlatformMapping()
102+
const assetName = `ripgrep-v${RG_VERSION}-${target}.${ext}`
103+
const downloadUrl = `${BASE_URL}/${assetName}`
104+
105+
const binaryPath = getBinaryPath()
106+
const binaryDir = path.dirname(binaryPath)
107+
108+
// Idempotent: skip if binary exists and has content
109+
const force = process.argv.includes('--force')
110+
if (!force && existsSync(binaryPath)) {
111+
const stat = statSync(binaryPath)
112+
if (stat.size > 0) {
113+
console.log(`[ripgrep] Binary already exists at ${binaryPath}, skipping.`)
114+
return
115+
}
116+
}
117+
118+
console.log(`[ripgrep] Downloading v${RG_VERSION} for ${target}...`)
119+
console.log(`[ripgrep] URL: ${downloadUrl}`)
120+
121+
// Prepare temp directory
122+
const tmpDir = path.join(binaryDir, '.tmp-download')
123+
rmSync(tmpDir, { recursive: true, force: true })
124+
mkdirSync(tmpDir, { recursive: true })
125+
126+
try {
127+
const archivePath = path.join(tmpDir, assetName)
128+
129+
// Download
130+
const response = await fetch(downloadUrl, { redirect: 'follow' })
131+
if (!response.ok) {
132+
throw new Error(`Download failed: ${response.status} ${response.statusText}`)
133+
}
134+
135+
const buffer = Buffer.from(await response.arrayBuffer())
136+
const { writeFileSync } = await import('fs')
137+
writeFileSync(archivePath, buffer)
138+
console.log(`[ripgrep] Downloaded ${Math.round(buffer.length / 1024)} KB`)
139+
140+
// Extract
141+
mkdirSync(binaryDir, { recursive: true })
142+
143+
if (ext === 'tar.gz') {
144+
const result = spawnSync('tar', ['xzf', archivePath, '-C', tmpDir], {
145+
stdio: 'pipe',
146+
})
147+
if (result.status !== 0) {
148+
throw new Error(`tar extract failed: ${result.stderr?.toString()}`)
149+
}
150+
} else {
151+
// .zip
152+
const result = spawnSync('unzip', ['-o', archivePath, '-d', tmpDir], {
153+
stdio: 'pipe',
154+
})
155+
if (result.status !== 0) {
156+
throw new Error(`unzip failed: ${result.stderr?.toString()}`)
157+
}
158+
}
159+
160+
// Find the rg binary in the extracted directory
161+
// microsoft/ripgrep-prebuilt archives extract flat: ./rg (no subdirectory)
162+
const extractedBinary = process.platform === 'win32' ? 'rg.exe' : 'rg'
163+
const srcBinary = path.join(tmpDir, extractedBinary)
164+
165+
if (!existsSync(srcBinary)) {
166+
throw new Error(`Binary not found at expected path: ${srcBinary}`)
167+
}
168+
169+
// Move to final location
170+
renameSync(srcBinary, binaryPath)
171+
172+
// Make executable (non-Windows)
173+
if (process.platform !== 'win32') {
174+
chmodSync(binaryPath, 0o755)
175+
}
176+
177+
console.log(`[ripgrep] Installed to ${binaryPath}`)
178+
} finally {
179+
// Cleanup temp directory
180+
rmSync(tmpDir, { recursive: true, force: true })
181+
}
182+
}
183+
184+
// --- Main ---
185+
186+
downloadAndExtract().catch(error => {
187+
console.error(`[ripgrep] Download failed: ${error.message}`)
188+
console.error(`[ripgrep] You can install ripgrep manually: https://github.com/BurntSushi/ripgrep#installation`)
189+
// Don't exit with error code — postinstall should not break bun install
190+
process.exit(0)
191+
})

0 commit comments

Comments
 (0)