The vendored-binary lookup at vendor/ripgrep/<arch>-<platform>/rg never resolved in this fork — that directory does not ship — so users without a system rg had no working fallback. Switch to the @vscode/ripgrep package so Microsoft maintains the platform/arch matrix and the binary is delivered via npm. - src/utils/ripgrep.ts: replace hand-rolled vendor-path resolution with rgPath from @vscode/ripgrep. Lazy require so a missing package falls through to the system rg branch instead of throwing at import. Drop builtinExists from the config args; builtinCommand is now a string-or-null. The system override (USE_BUILTIN_RIPGREP=0), the Bun-compiled standalone embedded mode, the macOS codesign hook, and all retry/timeout/error logic are preserved untouched. - scripts/build.ts: mark @vscode/ripgrep as external. The package resolves rgPath via __dirname at runtime, so bundling would freeze the build host's absolute path into dist/cli.mjs. - src/utils/ripgrep.test.ts: update for the new config shape and add tests covering USE_BUILTIN_RIPGREP=0, embedded mode, last-resort fallback, and null builtin path. Tested locally on Linux (Bun 1.3.13). macOS (codesign hook) and Windows (rg.exe extension) need contributor verification.
116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
import { expect, test } from 'bun:test'
|
|
import path from 'path'
|
|
|
|
import { resolveRipgrepConfig, wrapRipgrepUnavailableError } from './ripgrep.js'
|
|
|
|
const MOCK_BUILTIN_PATH = path.normalize(
|
|
process.platform === 'win32'
|
|
? `node_modules/@vscode/ripgrep/bin/rg.exe`
|
|
: `node_modules/@vscode/ripgrep/bin/rg`,
|
|
)
|
|
|
|
test('falls back to system rg when @vscode/ripgrep cannot be resolved', () => {
|
|
const config = resolveRipgrepConfig({
|
|
userWantsSystemRipgrep: false,
|
|
bundledMode: false,
|
|
builtinCommand: null,
|
|
systemExecutablePath: '/usr/bin/rg',
|
|
processExecPath: '/fake/bun',
|
|
})
|
|
|
|
expect(config).toMatchObject({
|
|
mode: 'system',
|
|
command: 'rg',
|
|
args: [],
|
|
})
|
|
})
|
|
|
|
test('uses builtin @vscode/ripgrep path when the package resolves', () => {
|
|
const config = resolveRipgrepConfig({
|
|
userWantsSystemRipgrep: false,
|
|
bundledMode: false,
|
|
builtinCommand: MOCK_BUILTIN_PATH,
|
|
systemExecutablePath: '/usr/bin/rg',
|
|
processExecPath: '/fake/bun',
|
|
})
|
|
|
|
expect(config).toMatchObject({
|
|
mode: 'builtin',
|
|
command: MOCK_BUILTIN_PATH,
|
|
args: [],
|
|
})
|
|
})
|
|
|
|
test('honors USE_BUILTIN_RIPGREP=0 by selecting system rg even when builtin is available', () => {
|
|
const config = resolveRipgrepConfig({
|
|
userWantsSystemRipgrep: true,
|
|
bundledMode: false,
|
|
builtinCommand: MOCK_BUILTIN_PATH,
|
|
systemExecutablePath: '/usr/bin/rg',
|
|
processExecPath: '/fake/bun',
|
|
})
|
|
|
|
expect(config).toMatchObject({
|
|
mode: 'system',
|
|
command: 'rg',
|
|
args: [],
|
|
})
|
|
})
|
|
|
|
test('keeps embedded mode for Bun-compiled standalone executables', () => {
|
|
const config = resolveRipgrepConfig({
|
|
userWantsSystemRipgrep: false,
|
|
bundledMode: true,
|
|
builtinCommand: null,
|
|
systemExecutablePath: '/usr/bin/rg',
|
|
processExecPath: '/opt/openclaude/bin/openclaude',
|
|
})
|
|
|
|
expect(config).toMatchObject({
|
|
mode: 'embedded',
|
|
command: '/opt/openclaude/bin/openclaude',
|
|
args: ['--no-config'],
|
|
argv0: 'rg',
|
|
})
|
|
})
|
|
|
|
test('falls through to system rg as a last resort even when not on PATH', () => {
|
|
const config = resolveRipgrepConfig({
|
|
userWantsSystemRipgrep: false,
|
|
bundledMode: false,
|
|
builtinCommand: null,
|
|
systemExecutablePath: 'rg',
|
|
processExecPath: '/fake/bun',
|
|
})
|
|
|
|
expect(config).toMatchObject({
|
|
mode: 'system',
|
|
command: 'rg',
|
|
args: [],
|
|
})
|
|
})
|
|
|
|
test('wrapRipgrepUnavailableError explains missing packaged fallback', () => {
|
|
const error = wrapRipgrepUnavailableError(
|
|
{ code: 'ENOENT', message: 'spawn rg ENOENT' },
|
|
{ mode: 'builtin', command: 'C:\\fake\\node_modules\\@vscode\\ripgrep\\bin\\rg.exe', args: [] },
|
|
'win32',
|
|
)
|
|
|
|
expect(error.name).toBe('RipgrepUnavailableError')
|
|
expect(error.code).toBe('ENOENT')
|
|
expect(error.message).toContain('packaged ripgrep fallback')
|
|
expect(error.message).toContain('winget install BurntSushi.ripgrep.MSVC')
|
|
})
|
|
|
|
test('wrapRipgrepUnavailableError explains missing system ripgrep', () => {
|
|
const error = wrapRipgrepUnavailableError(
|
|
{ code: 'ENOENT', message: 'spawn rg ENOENT' },
|
|
{ mode: 'system', command: 'rg', args: [] },
|
|
'linux',
|
|
)
|
|
|
|
expect(error.message).toContain('system ripgrep binary was not found on PATH')
|
|
expect(error.message).toContain('apt install ripgrep')
|
|
})
|