Skip to content

Commit 5944a39

Browse files
committed
feat(regexp/unstable): add replaceAllAsync
1 parent 9c5b710 commit 5944a39

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

regexp/deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "1.0.1",
44
"exports": {
55
".": "./mod.ts",
6-
"./escape": "./escape.ts"
6+
"./escape": "./escape.ts",
7+
"./unstable-replace-all-async": "./unstable_replace_all_async.ts"
78
}
89
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2018-2026 the Deno authors. MIT license.
2+
// deno-lint-ignore-file no-explicit-any
3+
4+
/**
5+
* Asynchronously replaces all occurrences of a pattern in a string.
6+
*
7+
* @experimental **UNSTABLE**: New API, yet to be vetted.
8+
*
9+
* @param text - The original string.
10+
* @param searchValue - The regular expression pattern to search for.
11+
* @param replacer - An asynchronous function that returns the replacement string.
12+
* @returns A promise that resolves to the modified string.
13+
*
14+
* @example
15+
* ```ts ignore
16+
* import { replaceAllAsync } from "@std/regexp/unstable-replace-all-async";
17+
* import { assertEquals } from "@std/assert";
18+
*
19+
* const result = await replaceAllAsync(
20+
* "https://example.com/ and https://example.com/not-found!",
21+
* /https:\/\/([\w\-/.]+)/g,
22+
* async (match, address) => {
23+
* const { status } = await fetch(match, { method: "HEAD" });
24+
* return `${address} returned status ${status}`;
25+
* },
26+
* );
27+
*
28+
* assertEquals(
29+
* result,
30+
* "example.com/ returned status 200 and example.com/not-found returned status 404!",
31+
* );
32+
* ```
33+
*/
34+
export async function replaceAllAsync(
35+
text: string,
36+
searchValue: RegExp | string,
37+
replacer: (substring: string, ...args: any[]) => Promise<string> | string,
38+
) {
39+
const promises: (Promise<string> | string)[] = [];
40+
41+
text.replaceAll(searchValue, (...args) => {
42+
promises.push(replacer(...args));
43+
return "";
44+
});
45+
46+
const results = (await Promise.all(promises))[Symbol.iterator]();
47+
return text.replaceAll(searchValue, () => results.next().value!);
48+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2018-2026 the Deno authors. MIT license.
2+
3+
import { replaceAllAsync } from "./unstable_replace_all_async.ts";
4+
import { assertEquals } from "@std/assert";
5+
import { stub } from "@std/testing/mock";
6+
7+
Deno.test(replaceAllAsync.name, async () => {
8+
using _ = stub(
9+
globalThis,
10+
"fetch",
11+
(u) =>
12+
Promise.resolve(
13+
{ status: String(u).includes("not-found") ? 404 : 200 } as Response,
14+
),
15+
);
16+
17+
const result = await replaceAllAsync(
18+
"https://example.com/ and https://example.com/not-found!",
19+
/https:\/\/([\w\-/.]+)/g,
20+
async (match, address) => {
21+
const { status } = await fetch(match, { method: "HEAD" });
22+
return `${address} returned status ${status}`;
23+
},
24+
);
25+
26+
assertEquals(
27+
result,
28+
"example.com/ returned status 200 and example.com/not-found returned status 404!",
29+
);
30+
});

0 commit comments

Comments
 (0)