ripgrep 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +52 -0
- package/README.md +79 -0
- package/lib/_rg.wasm.mjs +58 -0
- package/lib/_wasi.mjs +485 -0
- package/lib/index.d.mts +114 -0
- package/lib/index.mjs +71 -0
- package/lib/rg.mjs +9 -0
- package/package.json +26 -1
package/lib/_wasi.mjs
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
// Minimal WASI preview1 shim implementing only the 20 functions ripgrep imports.
|
|
2
|
+
// Backed by `node:fs` sync APIs, so it works on Node, Bun, and Deno uniformly.
|
|
3
|
+
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { randomFillSync } from "node:crypto";
|
|
7
|
+
|
|
8
|
+
// Errno (subset used here; full table at
|
|
9
|
+
// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md)
|
|
10
|
+
const E = {
|
|
11
|
+
SUCCESS: 0,
|
|
12
|
+
ACCES: 2,
|
|
13
|
+
BADF: 8,
|
|
14
|
+
EXIST: 20,
|
|
15
|
+
INVAL: 28,
|
|
16
|
+
IO: 29,
|
|
17
|
+
ISDIR: 31,
|
|
18
|
+
NAMETOOLONG: 37,
|
|
19
|
+
NOENT: 44,
|
|
20
|
+
NOSYS: 52,
|
|
21
|
+
NOTDIR: 54,
|
|
22
|
+
NOTSUP: 58,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Filetype enum
|
|
26
|
+
const FT = {
|
|
27
|
+
UNKNOWN: 0,
|
|
28
|
+
BLOCK_DEVICE: 1,
|
|
29
|
+
CHARACTER_DEVICE: 2,
|
|
30
|
+
DIRECTORY: 3,
|
|
31
|
+
REGULAR_FILE: 4,
|
|
32
|
+
SYMBOLIC_LINK: 7,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const PREOPENTYPE_DIR = 0;
|
|
36
|
+
const OFLAGS_CREAT = 1;
|
|
37
|
+
const OFLAGS_DIRECTORY = 2;
|
|
38
|
+
const OFLAGS_EXCL = 4;
|
|
39
|
+
const OFLAGS_TRUNC = 8;
|
|
40
|
+
const FDFLAGS_APPEND = 1;
|
|
41
|
+
const RIGHTS_FD_READ = 1n << 1n;
|
|
42
|
+
const RIGHTS_FD_WRITE = 1n << 6n;
|
|
43
|
+
|
|
44
|
+
class WASIExit extends Error {
|
|
45
|
+
constructor(code) {
|
|
46
|
+
super(`wasi exit: ${code}`);
|
|
47
|
+
this.code = code;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createWasi({ args, env, preopens, returnOnExit = true } = {}) {
|
|
52
|
+
const enc = new TextEncoder();
|
|
53
|
+
const dec = new TextDecoder();
|
|
54
|
+
|
|
55
|
+
const argBytes = args.map((a) => enc.encode(a + "\0"));
|
|
56
|
+
const envBytes = Object.entries(env)
|
|
57
|
+
.filter(([, v]) => v != null)
|
|
58
|
+
.map(([k, v]) => enc.encode(`${k}=${v}\0`));
|
|
59
|
+
|
|
60
|
+
// fds: 0/1/2 = stdio, 3+ = preopens, then files/dirs opened at runtime.
|
|
61
|
+
const fds = [
|
|
62
|
+
{ type: "stdio", which: 0 },
|
|
63
|
+
{ type: "stdio", which: 1 },
|
|
64
|
+
{ type: "stdio", which: 2 },
|
|
65
|
+
];
|
|
66
|
+
for (const [name, hostPath] of Object.entries(preopens)) {
|
|
67
|
+
fds.push({
|
|
68
|
+
type: "dir",
|
|
69
|
+
hostPath: path.resolve(hostPath),
|
|
70
|
+
preopenName: name,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let memory;
|
|
75
|
+
const dv = () => new DataView(memory.buffer);
|
|
76
|
+
const u8 = () => new Uint8Array(memory.buffer);
|
|
77
|
+
|
|
78
|
+
const imports = {
|
|
79
|
+
proc_exit(code) {
|
|
80
|
+
throw new WASIExit(code);
|
|
81
|
+
},
|
|
82
|
+
sched_yield() {
|
|
83
|
+
return E.SUCCESS;
|
|
84
|
+
},
|
|
85
|
+
poll_oneoff(_in, _out, _n, _neventsOut) {
|
|
86
|
+
// ripgrep doesn't actually need this for file search; stub it.
|
|
87
|
+
return E.NOTSUP;
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
args_sizes_get(argcPtr, bufSizePtr) {
|
|
91
|
+
const v = dv();
|
|
92
|
+
v.setUint32(argcPtr, argBytes.length, true);
|
|
93
|
+
v.setUint32(
|
|
94
|
+
bufSizePtr,
|
|
95
|
+
argBytes.reduce((s, b) => s + b.length, 0),
|
|
96
|
+
true,
|
|
97
|
+
);
|
|
98
|
+
return E.SUCCESS;
|
|
99
|
+
},
|
|
100
|
+
args_get(argvPtr, argvBufPtr) {
|
|
101
|
+
const v = dv();
|
|
102
|
+
const mem = u8();
|
|
103
|
+
for (const b of argBytes) {
|
|
104
|
+
v.setUint32(argvPtr, argvBufPtr, true);
|
|
105
|
+
argvPtr += 4;
|
|
106
|
+
mem.set(b, argvBufPtr);
|
|
107
|
+
argvBufPtr += b.length;
|
|
108
|
+
}
|
|
109
|
+
return E.SUCCESS;
|
|
110
|
+
},
|
|
111
|
+
environ_sizes_get(countPtr, bufSizePtr) {
|
|
112
|
+
const v = dv();
|
|
113
|
+
v.setUint32(countPtr, envBytes.length, true);
|
|
114
|
+
v.setUint32(
|
|
115
|
+
bufSizePtr,
|
|
116
|
+
envBytes.reduce((s, b) => s + b.length, 0),
|
|
117
|
+
true,
|
|
118
|
+
);
|
|
119
|
+
return E.SUCCESS;
|
|
120
|
+
},
|
|
121
|
+
environ_get(environPtr, environBufPtr) {
|
|
122
|
+
const v = dv();
|
|
123
|
+
const mem = u8();
|
|
124
|
+
for (const b of envBytes) {
|
|
125
|
+
v.setUint32(environPtr, environBufPtr, true);
|
|
126
|
+
environPtr += 4;
|
|
127
|
+
mem.set(b, environBufPtr);
|
|
128
|
+
environBufPtr += b.length;
|
|
129
|
+
}
|
|
130
|
+
return E.SUCCESS;
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
clock_time_get(id, _precision, timePtr) {
|
|
134
|
+
let t;
|
|
135
|
+
if (id === 0) {
|
|
136
|
+
t = BigInt(Date.now()) * 1_000_000n;
|
|
137
|
+
} else if (id === 1) {
|
|
138
|
+
t = process.hrtime.bigint
|
|
139
|
+
? process.hrtime.bigint()
|
|
140
|
+
: BigInt(Math.round(performance.now() * 1e6));
|
|
141
|
+
} else {
|
|
142
|
+
return E.INVAL;
|
|
143
|
+
}
|
|
144
|
+
dv().setBigUint64(timePtr, t, true);
|
|
145
|
+
return E.SUCCESS;
|
|
146
|
+
},
|
|
147
|
+
random_get(bufPtr, bufLen) {
|
|
148
|
+
randomFillSync(u8(), bufPtr, bufLen);
|
|
149
|
+
return E.SUCCESS;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
fd_read(fd, iovsPtr, iovsLen, nreadPtr) {
|
|
153
|
+
const e = fds[fd];
|
|
154
|
+
if (!e) return E.BADF;
|
|
155
|
+
const v = dv();
|
|
156
|
+
const mem = u8();
|
|
157
|
+
try {
|
|
158
|
+
let total = 0;
|
|
159
|
+
for (let i = 0; i < iovsLen; i++) {
|
|
160
|
+
const bufPtr = v.getUint32(iovsPtr + i * 8, true);
|
|
161
|
+
const bufLen = v.getUint32(iovsPtr + i * 8 + 4, true);
|
|
162
|
+
let n = 0;
|
|
163
|
+
if (e.type === "file") {
|
|
164
|
+
const target = Buffer.from(
|
|
165
|
+
mem.buffer,
|
|
166
|
+
mem.byteOffset + bufPtr,
|
|
167
|
+
bufLen,
|
|
168
|
+
);
|
|
169
|
+
n = fs.readSync(e.hostFd, target, 0, bufLen, null);
|
|
170
|
+
e.pos += BigInt(n);
|
|
171
|
+
} else if (e.type === "stdio" && e.which === 0) {
|
|
172
|
+
// No stdin support (would need blocking read). Signal EOF.
|
|
173
|
+
n = 0;
|
|
174
|
+
} else {
|
|
175
|
+
return E.BADF;
|
|
176
|
+
}
|
|
177
|
+
total += n;
|
|
178
|
+
if (n < bufLen) break;
|
|
179
|
+
}
|
|
180
|
+
v.setUint32(nreadPtr, total, true);
|
|
181
|
+
return E.SUCCESS;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
return errno(err);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
fd_write(fd, iovsPtr, iovsLen, nwrittenPtr) {
|
|
187
|
+
const e = fds[fd];
|
|
188
|
+
if (!e) return E.BADF;
|
|
189
|
+
const v = dv();
|
|
190
|
+
const mem = u8();
|
|
191
|
+
try {
|
|
192
|
+
let total = 0;
|
|
193
|
+
for (let i = 0; i < iovsLen; i++) {
|
|
194
|
+
const bufPtr = v.getUint32(iovsPtr + i * 8, true);
|
|
195
|
+
const bufLen = v.getUint32(iovsPtr + i * 8 + 4, true);
|
|
196
|
+
const chunk = mem.subarray(bufPtr, bufPtr + bufLen);
|
|
197
|
+
if (e.type === "stdio") {
|
|
198
|
+
if (e.which === 1) process.stdout.write(Buffer.from(chunk));
|
|
199
|
+
else if (e.which === 2) process.stderr.write(Buffer.from(chunk));
|
|
200
|
+
else return E.BADF;
|
|
201
|
+
total += bufLen;
|
|
202
|
+
} else if (e.type === "file") {
|
|
203
|
+
const n = fs.writeSync(e.hostFd, chunk, 0, bufLen, null);
|
|
204
|
+
e.pos += BigInt(n);
|
|
205
|
+
total += n;
|
|
206
|
+
if (n < bufLen) break;
|
|
207
|
+
} else {
|
|
208
|
+
return E.BADF;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
v.setUint32(nwrittenPtr, total, true);
|
|
212
|
+
return E.SUCCESS;
|
|
213
|
+
} catch (err) {
|
|
214
|
+
return errno(err);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
fd_close(fd) {
|
|
218
|
+
const e = fds[fd];
|
|
219
|
+
if (!e) return E.BADF;
|
|
220
|
+
try {
|
|
221
|
+
if (e.type === "file") fs.closeSync(e.hostFd);
|
|
222
|
+
fds[fd] = undefined;
|
|
223
|
+
return E.SUCCESS;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
return errno(err);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
fd_tell(fd, offsetPtr) {
|
|
229
|
+
const e = fds[fd];
|
|
230
|
+
if (!e || e.type !== "file") return E.BADF;
|
|
231
|
+
dv().setBigUint64(offsetPtr, e.pos, true);
|
|
232
|
+
return E.SUCCESS;
|
|
233
|
+
},
|
|
234
|
+
fd_readdir(fd, bufPtr, bufLen, cookie, bufUsedPtr) {
|
|
235
|
+
const e = fds[fd];
|
|
236
|
+
if (!e || e.type !== "dir") return E.BADF;
|
|
237
|
+
const v = dv();
|
|
238
|
+
const mem = u8();
|
|
239
|
+
try {
|
|
240
|
+
if (!e.dirents) {
|
|
241
|
+
const list = fs.readdirSync(e.hostPath, { withFileTypes: true });
|
|
242
|
+
e.dirents = list.map((d) => ({
|
|
243
|
+
name: d.name,
|
|
244
|
+
nameBytes: enc.encode(d.name),
|
|
245
|
+
type: direntType(d),
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
let used = 0;
|
|
249
|
+
const HEAD = 24;
|
|
250
|
+
for (let i = Number(cookie); i < e.dirents.length; i++) {
|
|
251
|
+
const d = e.dirents[i];
|
|
252
|
+
if (bufLen - used < HEAD) {
|
|
253
|
+
used = bufLen;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
v.setBigUint64(bufPtr + used + 0, BigInt(i + 1), true); // d_next
|
|
257
|
+
v.setBigUint64(bufPtr + used + 8, 0n, true); // d_ino
|
|
258
|
+
v.setUint32(bufPtr + used + 16, d.nameBytes.length, true); // d_namlen
|
|
259
|
+
v.setUint8(bufPtr + used + 20, d.type); // d_type
|
|
260
|
+
used += HEAD;
|
|
261
|
+
const space = Math.min(d.nameBytes.length, bufLen - used);
|
|
262
|
+
mem.set(d.nameBytes.subarray(0, space), bufPtr + used);
|
|
263
|
+
used += space;
|
|
264
|
+
if (space < d.nameBytes.length) {
|
|
265
|
+
used = bufLen;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
v.setUint32(bufUsedPtr, used, true);
|
|
270
|
+
return E.SUCCESS;
|
|
271
|
+
} catch (err) {
|
|
272
|
+
return errno(err);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
fd_filestat_get(fd, filestatPtr) {
|
|
276
|
+
const e = fds[fd];
|
|
277
|
+
if (!e) return E.BADF;
|
|
278
|
+
try {
|
|
279
|
+
if (e.type === "stdio") {
|
|
280
|
+
writeFilestat(dv(), filestatPtr, {
|
|
281
|
+
dev: 0n,
|
|
282
|
+
ino: 0n,
|
|
283
|
+
filetype: FT.CHARACTER_DEVICE,
|
|
284
|
+
nlink: 1n,
|
|
285
|
+
size: 0n,
|
|
286
|
+
atim: 0n,
|
|
287
|
+
mtim: 0n,
|
|
288
|
+
ctim: 0n,
|
|
289
|
+
});
|
|
290
|
+
return E.SUCCESS;
|
|
291
|
+
}
|
|
292
|
+
const st =
|
|
293
|
+
e.type === "file"
|
|
294
|
+
? fs.fstatSync(e.hostFd, { bigint: true })
|
|
295
|
+
: fs.statSync(e.hostPath, { bigint: true });
|
|
296
|
+
writeFilestat(dv(), filestatPtr, filestatFromNode(st));
|
|
297
|
+
return E.SUCCESS;
|
|
298
|
+
} catch (err) {
|
|
299
|
+
return errno(err);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
fd_fdstat_get(fd, fdstatPtr) {
|
|
303
|
+
const e = fds[fd];
|
|
304
|
+
if (!e) return E.BADF;
|
|
305
|
+
const v = dv();
|
|
306
|
+
let filetype = FT.UNKNOWN;
|
|
307
|
+
if (e.type === "stdio") filetype = FT.CHARACTER_DEVICE;
|
|
308
|
+
else if (e.type === "dir") filetype = FT.DIRECTORY;
|
|
309
|
+
else if (e.type === "file") filetype = FT.REGULAR_FILE;
|
|
310
|
+
v.setUint8(fdstatPtr + 0, filetype);
|
|
311
|
+
v.setUint8(fdstatPtr + 1, 0);
|
|
312
|
+
v.setUint16(fdstatPtr + 2, 0, true);
|
|
313
|
+
v.setUint32(fdstatPtr + 4, 0, true);
|
|
314
|
+
// Grant all rights — ripgrep only reads, so over-granting is harmless.
|
|
315
|
+
v.setBigUint64(fdstatPtr + 8, ~0n, true);
|
|
316
|
+
v.setBigUint64(fdstatPtr + 16, ~0n, true);
|
|
317
|
+
return E.SUCCESS;
|
|
318
|
+
},
|
|
319
|
+
fd_prestat_get(fd, prestatPtr) {
|
|
320
|
+
const e = fds[fd];
|
|
321
|
+
if (!e || e.type !== "dir" || !e.preopenName) return E.BADF;
|
|
322
|
+
const v = dv();
|
|
323
|
+
v.setUint8(prestatPtr + 0, PREOPENTYPE_DIR);
|
|
324
|
+
v.setUint32(prestatPtr + 4, enc.encode(e.preopenName).length, true);
|
|
325
|
+
return E.SUCCESS;
|
|
326
|
+
},
|
|
327
|
+
fd_prestat_dir_name(fd, pathPtr, pathLen) {
|
|
328
|
+
const e = fds[fd];
|
|
329
|
+
if (!e || e.type !== "dir" || !e.preopenName) return E.BADF;
|
|
330
|
+
const name = enc.encode(e.preopenName);
|
|
331
|
+
if (name.length > pathLen) return E.NAMETOOLONG;
|
|
332
|
+
u8().set(name, pathPtr);
|
|
333
|
+
return E.SUCCESS;
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
path_open(
|
|
337
|
+
dirfd,
|
|
338
|
+
_dirflags,
|
|
339
|
+
pathPtr,
|
|
340
|
+
pathLen,
|
|
341
|
+
oflags,
|
|
342
|
+
fsRightsBase,
|
|
343
|
+
_fsRightsInheriting,
|
|
344
|
+
fdflags,
|
|
345
|
+
openedFdPtr,
|
|
346
|
+
) {
|
|
347
|
+
const e = fds[dirfd];
|
|
348
|
+
if (!e || e.type !== "dir") return E.BADF;
|
|
349
|
+
const v = dv();
|
|
350
|
+
const relPath = dec.decode(u8().subarray(pathPtr, pathPtr + pathLen));
|
|
351
|
+
const fullPath = path.resolve(e.hostPath, relPath);
|
|
352
|
+
try {
|
|
353
|
+
let st;
|
|
354
|
+
try {
|
|
355
|
+
st = fs.statSync(fullPath);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
// Only swallow ENOENT when O_CREAT is set; propagate everything else.
|
|
358
|
+
if (!(oflags & OFLAGS_CREAT) || err?.code !== "ENOENT")
|
|
359
|
+
return errno(err);
|
|
360
|
+
}
|
|
361
|
+
if (st?.isDirectory()) {
|
|
362
|
+
fds.push({ type: "dir", hostPath: fullPath });
|
|
363
|
+
v.setUint32(openedFdPtr, fds.length - 1, true);
|
|
364
|
+
return E.SUCCESS;
|
|
365
|
+
}
|
|
366
|
+
if (oflags & OFLAGS_DIRECTORY) return E.NOTDIR;
|
|
367
|
+
|
|
368
|
+
let flags = 0;
|
|
369
|
+
const canRead = (BigInt(fsRightsBase) & RIGHTS_FD_READ) !== 0n;
|
|
370
|
+
const canWrite = (BigInt(fsRightsBase) & RIGHTS_FD_WRITE) !== 0n;
|
|
371
|
+
if (canRead && canWrite) flags |= fs.constants.O_RDWR;
|
|
372
|
+
else if (canWrite) flags |= fs.constants.O_WRONLY;
|
|
373
|
+
else flags |= fs.constants.O_RDONLY;
|
|
374
|
+
if (oflags & OFLAGS_CREAT) flags |= fs.constants.O_CREAT;
|
|
375
|
+
if (oflags & OFLAGS_EXCL) flags |= fs.constants.O_EXCL;
|
|
376
|
+
if (oflags & OFLAGS_TRUNC) flags |= fs.constants.O_TRUNC;
|
|
377
|
+
if (fdflags & FDFLAGS_APPEND) flags |= fs.constants.O_APPEND;
|
|
378
|
+
|
|
379
|
+
const hostFd = fs.openSync(fullPath, flags);
|
|
380
|
+
fds.push({ type: "file", hostFd, pos: 0n });
|
|
381
|
+
v.setUint32(openedFdPtr, fds.length - 1, true);
|
|
382
|
+
return E.SUCCESS;
|
|
383
|
+
} catch (err) {
|
|
384
|
+
return errno(err);
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
path_filestat_get(dirfd, flags, pathPtr, pathLen, filestatPtr) {
|
|
388
|
+
const e = fds[dirfd];
|
|
389
|
+
if (!e || e.type !== "dir") return E.BADF;
|
|
390
|
+
const relPath = dec.decode(u8().subarray(pathPtr, pathPtr + pathLen));
|
|
391
|
+
const fullPath = path.resolve(e.hostPath, relPath);
|
|
392
|
+
try {
|
|
393
|
+
// bit 0 of lookupflags = symlink_follow
|
|
394
|
+
const follow = (flags & 1) !== 0;
|
|
395
|
+
const st = follow
|
|
396
|
+
? fs.statSync(fullPath, { bigint: true })
|
|
397
|
+
: fs.lstatSync(fullPath, { bigint: true });
|
|
398
|
+
writeFilestat(dv(), filestatPtr, filestatFromNode(st));
|
|
399
|
+
return E.SUCCESS;
|
|
400
|
+
} catch (err) {
|
|
401
|
+
return errno(err);
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
imports: { wasi_snapshot_preview1: imports },
|
|
408
|
+
start(instance) {
|
|
409
|
+
memory = instance.exports.memory;
|
|
410
|
+
try {
|
|
411
|
+
instance.exports._start();
|
|
412
|
+
return 0;
|
|
413
|
+
} catch (err) {
|
|
414
|
+
if (err instanceof WASIExit) {
|
|
415
|
+
if (returnOnExit) return err.code;
|
|
416
|
+
throw err;
|
|
417
|
+
}
|
|
418
|
+
throw err;
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// --- internal helpers -------------------------------------------------------
|
|
425
|
+
|
|
426
|
+
function writeFilestat(v, ptr, s) {
|
|
427
|
+
v.setBigUint64(ptr + 0, s.dev, true);
|
|
428
|
+
v.setBigUint64(ptr + 8, s.ino, true);
|
|
429
|
+
v.setUint8(ptr + 16, s.filetype);
|
|
430
|
+
v.setBigUint64(ptr + 24, s.nlink, true);
|
|
431
|
+
v.setBigUint64(ptr + 32, s.size, true);
|
|
432
|
+
v.setBigUint64(ptr + 40, s.atim, true);
|
|
433
|
+
v.setBigUint64(ptr + 48, s.mtim, true);
|
|
434
|
+
v.setBigUint64(ptr + 56, s.ctim, true);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function filestatFromNode(st) {
|
|
438
|
+
let filetype = FT.UNKNOWN;
|
|
439
|
+
if (st.isFile()) filetype = FT.REGULAR_FILE;
|
|
440
|
+
else if (st.isDirectory()) filetype = FT.DIRECTORY;
|
|
441
|
+
else if (st.isSymbolicLink()) filetype = FT.SYMBOLIC_LINK;
|
|
442
|
+
else if (st.isBlockDevice()) filetype = FT.BLOCK_DEVICE;
|
|
443
|
+
else if (st.isCharacterDevice()) filetype = FT.CHARACTER_DEVICE;
|
|
444
|
+
return {
|
|
445
|
+
dev: BigInt(st.dev),
|
|
446
|
+
ino: BigInt(st.ino),
|
|
447
|
+
filetype,
|
|
448
|
+
nlink: BigInt(st.nlink),
|
|
449
|
+
size: BigInt(st.size),
|
|
450
|
+
atim: BigInt(st.atimeNs ?? 0),
|
|
451
|
+
mtim: BigInt(st.mtimeNs ?? 0),
|
|
452
|
+
ctim: BigInt(st.ctimeNs ?? 0),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function direntType(d) {
|
|
457
|
+
if (d.isFile()) return FT.REGULAR_FILE;
|
|
458
|
+
if (d.isDirectory()) return FT.DIRECTORY;
|
|
459
|
+
if (d.isSymbolicLink()) return FT.SYMBOLIC_LINK;
|
|
460
|
+
if (d.isBlockDevice()) return FT.BLOCK_DEVICE;
|
|
461
|
+
if (d.isCharacterDevice()) return FT.CHARACTER_DEVICE;
|
|
462
|
+
return FT.UNKNOWN;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function errno(err) {
|
|
466
|
+
switch (err?.code) {
|
|
467
|
+
case "ENOENT":
|
|
468
|
+
return E.NOENT;
|
|
469
|
+
case "EBADF":
|
|
470
|
+
return E.BADF;
|
|
471
|
+
case "EACCES":
|
|
472
|
+
case "EPERM":
|
|
473
|
+
return E.ACCES;
|
|
474
|
+
case "EISDIR":
|
|
475
|
+
return E.ISDIR;
|
|
476
|
+
case "ENOTDIR":
|
|
477
|
+
return E.NOTDIR;
|
|
478
|
+
case "EEXIST":
|
|
479
|
+
return E.EXIST;
|
|
480
|
+
case "EINVAL":
|
|
481
|
+
return E.INVAL;
|
|
482
|
+
default:
|
|
483
|
+
return E.IO;
|
|
484
|
+
}
|
|
485
|
+
}
|
package/lib/index.d.mts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known ripgrep CLI flags (long + short forms). Used to power
|
|
3
|
+
* autocomplete for {@link ripgrep} arguments — any string is still
|
|
4
|
+
* accepted (values, patterns, paths, unknown flags).
|
|
5
|
+
*/
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
export type RgFlag =
|
|
8
|
+
| "--after-context" | "--auto-hybrid-regex" | "--before-context" | "--binary"
|
|
9
|
+
| "--block-buffered" | "--byte-offset" | "--case-sensitive" | "--color"
|
|
10
|
+
| "--colors" | "--column" | "--context" | "--context-separator" | "--count"
|
|
11
|
+
| "--count-matches" | "--crlf" | "--debug" | "--dfa-size-limit" | "--encoding"
|
|
12
|
+
| "--engine" | "--field-context-separator" | "--field-match-separator"
|
|
13
|
+
| "--file" | "--files" | "--files-without-match" | "--fixed-strings"
|
|
14
|
+
| "--follow" | "--glob" | "--glob-case-insensitive" | "--heading" | "--help"
|
|
15
|
+
| "--hidden" | "--hostname-bin" | "--hyperlink-format" | "--iglob" | "--ignore"
|
|
16
|
+
| "--ignore-case" | "--ignore-dot" | "--ignore-exclude" | "--ignore-file"
|
|
17
|
+
| "--ignore-file-case-insensitive" | "--ignore-files" | "--ignore-global"
|
|
18
|
+
| "--ignore-parent" | "--ignore-vcs" | "--include-zero" | "--invert-match"
|
|
19
|
+
| "--json" | "--line-buffered" | "--line-number" | "--line-regexp"
|
|
20
|
+
| "--max-columns" | "--max-columns-preview" | "--max-count" | "--max-depth"
|
|
21
|
+
| "--max-filesize" | "--maxdepth" | "--mmap" | "--multiline" | "--multiline-dotall"
|
|
22
|
+
| "--no-auto-hybrid-regex" | "--no-binary" | "--no-block-buffered" | "--no-byte-offset"
|
|
23
|
+
| "--no-column" | "--no-context-separator" | "--no-crlf" | "--no-encoding"
|
|
24
|
+
| "--no-filename" | "--no-fixed-strings" | "--no-follow" | "--no-glob-case-insensitive"
|
|
25
|
+
| "--no-heading" | "--no-hidden" | "--no-ignore" | "--no-ignore-dot"
|
|
26
|
+
| "--no-ignore-exclude" | "--no-ignore-file-case-insensitive" | "--no-ignore-files"
|
|
27
|
+
| "--no-ignore-global" | "--no-ignore-parent" | "--no-ignore-vcs" | "--no-include-zero"
|
|
28
|
+
| "--no-invert-match" | "--no-line-buffered" | "--no-line-number"
|
|
29
|
+
| "--no-max-columns-preview" | "--no-messages" | "--no-mmap" | "--no-multiline"
|
|
30
|
+
| "--no-multiline-dotall" | "--no-one-file-system" | "--no-pcre2" | "--no-pcre2-unicode"
|
|
31
|
+
| "--no-pre" | "--no-require-git" | "--no-search-zip" | "--no-sort-files" | "--no-text"
|
|
32
|
+
| "--no-trim" | "--no-unicode" | "--null" | "--null-data" | "--one-file-system"
|
|
33
|
+
| "--only-matching" | "--passthrough" | "--passthru" | "--path-separator" | "--pcre2"
|
|
34
|
+
| "--pcre2-unicode" | "--pre" | "--pre-glob" | "--pretty" | "--print0" | "--quiet"
|
|
35
|
+
| "--regex-size-limit" | "--regexp" | "--replace" | "--require-git" | "--search-zip"
|
|
36
|
+
| "--smart-case" | "--sort" | "--sort-files" | "--sortr" | "--stop-on-nonmatch"
|
|
37
|
+
| "--text" | "--threads" | "--trim" | "--type" | "--type-add" | "--type-clear"
|
|
38
|
+
| "--type-list" | "--type-not" | "--unicode" | "--unrestricted" | "--version"
|
|
39
|
+
| "--vimgrep" | "--with-filename" | "--word-regexp"
|
|
40
|
+
| "-A" | "-B" | "-C" | "-E" | "-F" | "-H" | "-I" | "-L" | "-M" | "-N" | "-P"
|
|
41
|
+
| "-R" | "-S" | "-T" | "-U"
|
|
42
|
+
| "-a" | "-b" | "-c" | "-d" | "-e" | "-f" | "-g" | "-h" | "-i" | "-j" | "-l"
|
|
43
|
+
| "-m" | "-n" | "-o" | "-p" | "-q" | "-r" | "-s" | "-t" | "-u" | "-v" | "-w"
|
|
44
|
+
| "-x" | "-z";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* An argument to {@link ripgrep}: a known ripgrep flag (autocompleted)
|
|
48
|
+
* or any other string (value, pattern, path, `--flag=value`, etc).
|
|
49
|
+
*/
|
|
50
|
+
export type RgArg = RgFlag | (string & {});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Options for {@link ripgrep}.
|
|
54
|
+
*/
|
|
55
|
+
export interface ripgrepOptions {
|
|
56
|
+
/**
|
|
57
|
+
* Environment variables passed to the WASI instance.
|
|
58
|
+
* @default process.env
|
|
59
|
+
*/
|
|
60
|
+
env?: Record<string, string | undefined>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* WASI preopened directories, mapping guest paths to host paths.
|
|
64
|
+
* Required for ripgrep to see any files on disk.
|
|
65
|
+
* @default { ".": process.cwd() }
|
|
66
|
+
*/
|
|
67
|
+
preopens?: Record<string, string>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* When `true`, WASI `proc_exit` returns the exit code from `start()`
|
|
71
|
+
* instead of terminating the Node process.
|
|
72
|
+
* @default true
|
|
73
|
+
*/
|
|
74
|
+
returnOnExit?: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Use Node's built-in `node:wasi` module instead of the bundled
|
|
78
|
+
* custom WASI shim. Also enabled via `ZIGREP_NODE_WASI=1`.
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
nodeWasi?: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Result of a ripgrep invocation.
|
|
86
|
+
*/
|
|
87
|
+
export interface RipgrepResult {
|
|
88
|
+
/** Exit code: 0 = matches found, 1 = no matches, 2 = error. */
|
|
89
|
+
code: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Run ripgrep (compiled to `wasm32-wasip1`) with the given CLI arguments.
|
|
94
|
+
*
|
|
95
|
+
* Returns the ripgrep exit code (0 = matches found, 1 = no matches,
|
|
96
|
+
* 2 = error), matching the native `rg` binary.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```js
|
|
100
|
+
* import { ripgrep } from "zigrep";
|
|
101
|
+
* const { code } = await ripgrep(["--json", "TODO", "src"]);
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function ripgrep(
|
|
105
|
+
args?: readonly RgArg[],
|
|
106
|
+
options?: ripgrepOptions,
|
|
107
|
+
): Promise<RipgrepResult>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Absolute filesystem path to a JS shim that runs ripgrep via `ripgrep`.
|
|
111
|
+
* Useful for tools that expect an `rgPath`-style binary path (e.g.
|
|
112
|
+
* `vscode-ripgrep`-compatible consumers).
|
|
113
|
+
*/
|
|
114
|
+
export const rgPath: string;
|
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
|
|
3
|
+
export const rgPath = fileURLToPath(new URL("./rg.mjs", import.meta.url));
|
|
4
|
+
|
|
5
|
+
export async function ripgrep(args = [], options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
env = process.env,
|
|
8
|
+
preopens = { ".": process.cwd() },
|
|
9
|
+
returnOnExit = true,
|
|
10
|
+
nodeWasi = process.env.ZIGREP_NODE_WASI === "1",
|
|
11
|
+
} = options;
|
|
12
|
+
|
|
13
|
+
// ripgrep's TTY auto-detection doesn't work through WASI preview1, so it
|
|
14
|
+
// defaults to --color=never. If the host stdout is a TTY and the caller
|
|
15
|
+
// hasn't picked a color mode themselves, force ANSI colors on.
|
|
16
|
+
const hasColorFlag = args.some(
|
|
17
|
+
(a) => a === "--color" || a.startsWith("--color=") || a === "--no-color",
|
|
18
|
+
);
|
|
19
|
+
if (!hasColorFlag && process.stdout.isTTY) {
|
|
20
|
+
args = ["--color=ansi", ...args];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const wasi = await (nodeWasi ? createNodeWasi : createWasiShim)({
|
|
24
|
+
args,
|
|
25
|
+
env,
|
|
26
|
+
preopens,
|
|
27
|
+
returnOnExit,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const wasm = await getRgWasmModule();
|
|
31
|
+
const instance = await WebAssembly.instantiate(wasm, wasi.imports);
|
|
32
|
+
const code = await wasi.start(instance);
|
|
33
|
+
return { code };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Compiling the wasm module is expensive; cache it so repeated `ripgrep` calls
|
|
37
|
+
// only pay the cost once. Instances are still created per-call since they're
|
|
38
|
+
// stateful (own memory, wasi context, etc).
|
|
39
|
+
let rgWasmModulePromise;
|
|
40
|
+
function getRgWasmModule() {
|
|
41
|
+
if (!rgWasmModulePromise) {
|
|
42
|
+
rgWasmModulePromise = import("./_rg.wasm.mjs").then(({ getRgWasmBytes }) =>
|
|
43
|
+
WebAssembly.compile(getRgWasmBytes()),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return rgWasmModulePromise;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Custom WASI preview1 shim (see `_wasi.mjs`). Lazy-imported so consumers
|
|
50
|
+
// that only touch `rgPath` don't pay for loading it.
|
|
51
|
+
async function createWasiShim({ args, env, preopens, returnOnExit }) {
|
|
52
|
+
const { createWasi } = await import("./_wasi.mjs");
|
|
53
|
+
return createWasi({ args: ["rg", ...args], env, preopens, returnOnExit });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Thin adapter over Node's built-in `node:wasi` (https://nodejs.org/api/wasi.html)
|
|
57
|
+
// so it plugs into the same `{ imports, start }` shape as the custom shim.
|
|
58
|
+
async function createNodeWasi({ args, env, preopens, returnOnExit }) {
|
|
59
|
+
const { WASI } = await import("node:wasi");
|
|
60
|
+
const wasi = new WASI({
|
|
61
|
+
version: "preview1",
|
|
62
|
+
args: ["rg", ...args],
|
|
63
|
+
env,
|
|
64
|
+
preopens,
|
|
65
|
+
returnOnExit,
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
imports: wasi.getImportObject(),
|
|
69
|
+
start: (instance) => wasi.start(instance) ?? 0,
|
|
70
|
+
};
|
|
71
|
+
}
|
package/lib/rg.mjs
ADDED