-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathcode-guard.mjs
More file actions
27 lines (25 loc) · 1.5 KB
/
Copy pathcode-guard.mjs
File metadata and controls
27 lines (25 loc) · 1.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Rewriting rendered HTML without touching code.
//
// An inline code span is escaped with `escapeMarkup`, which keeps quotes, so
// `id="`, `href="#` and `src="` all match inside a code sample, and a
// rewrite over a whole rendered page that does not skip code corrupts the
// sample. A highlighted block is protected only by accident, because the
// highlighter splits attributes across `<span>` boundaries; nothing should
// rest on that.
//
// The guard is a leading alternative. CODE_OR_PRE comes first in the pattern
// and consumes a `<code>` or `<pre>` element whole, so the rest of the
// pattern never sees inside one, and a match of it is kept as it is. Compose
// a pattern from `CODE_OR_PRE.source`, or give replaceOutsideCode the bare
// pattern.
export const CODE_OR_PRE = /<code\b[^>]*>[\s\S]*?<\/code>|<pre\b[^>]*>[\s\S]*?<\/pre>/;
// `html` with each match of `pattern` outside code replaced by `replacer`,
// which is called as `String.prototype.replace` calls it. Every match is
// replaced, whether or not `pattern` has the `g` flag. The guard takes
// `pattern`'s flags, so under `i` it also keeps `<PRE>` and `<Code>`.
export function replaceOutsideCode(html, pattern, replacer) {
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
const re = new RegExp(`${CODE_OR_PRE.source}|${pattern.source}`, flags);
const guarded = flags.includes("i") ? /^<(?:code|pre)/i : /^<(?:code|pre)/;
return html.replace(re, (m, ...rest) => (guarded.test(m) ? m : replacer(m, ...rest)));
}