Skip to content

Commit 8666e19

Browse files
committed
revert: drop @vscode/ripgrep require attempt for now
Diagnostic from a Windows VS Code stable 1.121.0 install (see #248 review thread) showed that require("@vscode/ripgrep") throws — VS Code's extHost interceptor aliases the require to @vscode/ripgrep-universal, but that package isn't installed on builds that haven't completed the package-rename migration (which includes current stable). The path-probe fallback in the diagnostic test build was what actually resolved ripgrep on that install. Reverts to the prior shape: hardcoded paths covering both the @vscode/ripgrep and @vscode/ripgrep-universal layouts under vscode.env.appRoot. Also drops the @vscode/ripgrep devDep, the esbuild external entry, the loadRipgrep wrapper file, and the tests that mocked it — all dead with the revert. VS Code's package-rename migration will be tracked in a separate issue; once it lands across stable + Insiders the require approach can be revisited with empirical evidence of which mechanism VS Code expects 3rd-party extensions to use.
1 parent 06c62c2 commit 8666e19

5 files changed

Lines changed: 49 additions & 51 deletions

File tree

pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/esbuild.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async function main() {
126126
// global-agent must be external because it dynamically patches Node.js http/https modules
127127
// which breaks when bundled. It needs access to the actual Node.js module instances.
128128
// undici must be bundled because our VSIX is packaged with `--no-dependencies`.
129-
external: ["vscode", "esbuild", "global-agent", "@vscode/ripgrep"],
129+
external: ["vscode", "esbuild", "global-agent"],
130130
}
131131

132132
/**

src/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,6 @@
554554
"@types/tmp": "^0.2.6",
555555
"@types/turndown": "^5.0.5",
556556
"@types/vscode": "^1.84.0",
557-
"@vscode/ripgrep": "^1.17.0",
558557
"@vscode/test-electron": "^2.5.2",
559558
"@vscode/vsce": "3.3.2",
560559
"ai": "^6.0.75",

src/services/ripgrep/__tests__/index.spec.ts

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
// npx vitest run src/services/ripgrep/__tests__/index.spec.ts
22

3+
import path from "path"
34
import { vi, describe, it, expect, beforeEach } from "vitest"
45

56
import { truncateLine, getBinPath } from "../index"
7+
import { fileExistsAtPath } from "../../../utils/fs"
68

7-
const ripgrepMock = vi.hoisted(() => ({ value: undefined as string | undefined, throws: false }))
8-
9-
vi.mock("@vscode/ripgrep", () => ({
10-
get rgPath() {
11-
if (ripgrepMock.throws) {
12-
throw new Error("simulated @vscode/ripgrep failure")
13-
}
14-
return ripgrepMock.value
15-
},
9+
vi.mock("../../../utils/fs", () => ({
10+
fileExistsAtPath: vi.fn(),
1611
}))
1712

13+
const mockFileExists = vi.mocked(fileExistsAtPath)
14+
1815
describe("Ripgrep line truncation", () => {
1916
// The default MAX_LINE_LENGTH is 500 in the implementation
2017
const MAX_LINE_LENGTH = 500
@@ -63,34 +60,32 @@ describe("Ripgrep line truncation", () => {
6360
})
6461

6562
describe("getBinPath", () => {
66-
beforeEach(() => {
67-
ripgrepMock.value = undefined
68-
ripgrepMock.throws = false
69-
})
70-
71-
it("returns the rgPath exported by @vscode/ripgrep", async () => {
72-
ripgrepMock.value = "/path/to/rg"
63+
const appRoot = "/fake/vscode/appRoot"
64+
const binName = process.platform.startsWith("win") ? "rg.exe" : "rg"
65+
const platformDir = `${process.platform}-${process.arch}`
7366

74-
expect(await getBinPath("/ignored")).toBe("/path/to/rg")
67+
beforeEach(() => {
68+
mockFileExists.mockReset()
69+
mockFileExists.mockResolvedValue(false)
7570
})
7671

77-
it("rewrites node_modules.asar to node_modules.asar.unpacked", async () => {
78-
ripgrepMock.value = "/app/node_modules.asar/@vscode/ripgrep-universal/bin/win32-x64/rg.exe"
72+
it("resolves ripgrep from the classic @vscode/ripgrep layout", async () => {
73+
const rg = path.join(appRoot, "node_modules/@vscode/ripgrep/bin", binName)
74+
mockFileExists.mockImplementation(async (p: string) => p === rg)
7975

80-
expect(await getBinPath("/ignored")).toBe(
81-
"/app/node_modules.asar.unpacked/@vscode/ripgrep-universal/bin/win32-x64/rg.exe",
82-
)
76+
expect(await getBinPath(appRoot)).toBe(rg)
8377
})
8478

85-
it("returns undefined when rgPath is not exported", async () => {
86-
ripgrepMock.value = undefined
79+
it("resolves ripgrep from the @vscode/ripgrep-universal layout (VS Code Insiders)", async () => {
80+
const rg = path.join(appRoot, "node_modules/@vscode/ripgrep-universal/bin", platformDir, binName)
81+
mockFileExists.mockImplementation(async (p: string) => p === rg)
8782

88-
expect(await getBinPath("/ignored")).toBeUndefined()
83+
expect(await getBinPath(appRoot)).toBe(rg)
8984
})
9085

91-
it("returns undefined when rgPath resolution throws", async () => {
92-
ripgrepMock.throws = true
86+
it("returns undefined when ripgrep cannot be found", async () => {
87+
mockFileExists.mockResolvedValue(false)
9388

94-
expect(await getBinPath("/ignored")).toBeUndefined()
89+
expect(await getBinPath(appRoot)).toBeUndefined()
9590
})
9691
})

src/services/ripgrep/index.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import * as readline from "readline"
55
import * as vscode from "vscode"
66

77
import { RooIgnoreController } from "../../core/ignore/RooIgnoreController"
8+
import { fileExistsAtPath } from "../../utils/fs"
89
/*
910
This file provides functionality to perform regex searches on files using ripgrep.
1011
Inspired by: https://github.com/DiscreteTom/vscode-ripgrep-utils
1112
1213
Key components:
13-
1. getBinPath: Resolves the ripgrep binary via the `@vscode/ripgrep` package — VS Code's require interceptor aliases it to the bundled binary at runtime.
14+
1. getBinPath: Locates the ripgrep binary (in the VS Code install, or on the system PATH).
1415
2. execRipgrep: Executes the ripgrep command and returns the output.
1516
3. regexSearchFiles: The main function that performs regex searches on files.
1617
- Parameters:
@@ -50,6 +51,11 @@ rel/path/to/helper.ts
5051
const isWindows = process.platform.startsWith("win")
5152
const binName = isWindows ? "rg.exe" : "rg"
5253

54+
// VS Code's @vscode/ripgrep-universal package (used by recent VS Code builds,
55+
// including the Insiders staged-install layout) nests the binary under
56+
// bin/<platform>-<arch>/ rather than directly in bin/.
57+
const ripgrepUniversalBinDir = `bin/${process.platform}-${process.arch}`
58+
5359
interface SearchFileResult {
5460
file: string
5561
searchResults: SearchResult[]
@@ -79,27 +85,28 @@ export function truncateLine(line: string, maxLength: number = MAX_LINE_LENGTH):
7985
return line.length > maxLength ? line.substring(0, maxLength) + " [truncated...]" : line
8086
}
8187
/**
82-
* Get the path to the ripgrep binary.
88+
* Get the path to the ripgrep binary shipped inside the VS Code installation.
8389
*
84-
* Imports `@vscode/ripgrep` and returns its `rgPath` export. In the extension
85-
* host this import is intercepted by VS Code (see microsoft/vscode
86-
* `src/vs/workbench/api/common/extHostRequireInterceptor.ts`) and aliased to
87-
* VS Code's own `@vscode/ripgrep-universal`, so we inherit whatever ripgrep
88-
* VS Code ships with — including the Insiders staged-install layout that the
89-
* old hardcoded path list missed (microsoft/vscode#252063). The
90-
* `node_modules.asar` → `node_modules.asar.unpacked` substitution mirrors
91-
* VS Code's own resolution in `src/vs/base/node/ripgrep.ts`.
90+
* Both the long-standing `@vscode/ripgrep` layout and the newer
91+
* `@vscode/ripgrep-universal` layout are checked — the latter is what VS Code
92+
* Insiders' staged-install builds use (see microsoft/vscode#252063).
9293
*
93-
* The `vscodeAppRoot` parameter is retained for API stability and ignored.
94-
* Returns `undefined` if `@vscode/ripgrep` is unavailable.
94+
* Returns `undefined` when ripgrep cannot be located.
9595
*/
96-
export async function getBinPath(_vscodeAppRoot: string): Promise<string | undefined> {
97-
try {
98-
const m = await import("@vscode/ripgrep")
99-
return m.rgPath?.replace(/\bnode_modules\.asar\b/, "node_modules.asar.unpacked")
100-
} catch {
101-
return undefined
96+
export async function getBinPath(vscodeAppRoot: string): Promise<string | undefined> {
97+
const checkPath = async (pkgFolder: string) => {
98+
const fullPath = path.join(vscodeAppRoot, pkgFolder, binName)
99+
return (await fileExistsAtPath(fullPath)) ? fullPath : undefined
102100
}
101+
102+
return (
103+
(await checkPath("node_modules/@vscode/ripgrep/bin/")) ||
104+
(await checkPath("node_modules/vscode-ripgrep/bin")) ||
105+
(await checkPath("node_modules.asar.unpacked/vscode-ripgrep/bin/")) ||
106+
(await checkPath("node_modules.asar.unpacked/@vscode/ripgrep/bin/")) ||
107+
(await checkPath(`node_modules/@vscode/ripgrep-universal/${ripgrepUniversalBinDir}`)) ||
108+
(await checkPath(`node_modules.asar.unpacked/@vscode/ripgrep-universal/${ripgrepUniversalBinDir}`))
109+
)
103110
}
104111

105112
async function execRipgrep(bin: string, args: string[]): Promise<string> {

0 commit comments

Comments
 (0)