Skip to content

Commit 2a2bc5f

Browse files
committed
refactor: Share escaping logic between escape fns
And narrow the maps for `escapeAttribute` and `escapeText`.
1 parent 0af5d6d commit 2a2bc5f

1 file changed

Lines changed: 43 additions & 43 deletions

File tree

src/encode.ts

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ import { encodeHTMLTrieRe, getCodePoint } from "./encode-trie";
22

33
const htmlReplacer = /[\t\n!-,./:-@[-`\f{-}$\x80-\uFFFF]/g;
44
const xmlReplacer = /["&'<>$\x80-\uFFFF]/g;
5-
const xmlInvalidChars = /[&<>'"]/g;
6-
7-
const textReplacer = /[&<>\u00A0]/g;
8-
const attrReplacer = /["&\u00A0]/g;
95

106
const xmlCodeMap = new Map([
117
[34, "&quot;"],
@@ -15,14 +11,6 @@ const xmlCodeMap = new Map([
1511
[62, "&gt;"],
1612
]);
1713

18-
const htmlEscapeCodeMap = new Map([
19-
[34, "&quot;"],
20-
[38, "&amp;"],
21-
[60, "&lt;"],
22-
[62, "&gt;"],
23-
[160, "&nbsp;"],
24-
]);
25-
2614
/**
2715
* Encodes all non-ASCII characters, as well as characters not valid in XML
2816
* documents using XML entities.
@@ -93,55 +81,67 @@ export function encodeNonAsciiHTML(data: string): string {
9381
*/
9482
export const escape = encodeXML;
9583

84+
function getEscaper(
85+
regex: RegExp,
86+
map: Map<number, string>
87+
): (data: string) => string {
88+
return function escape(data: string): string {
89+
let match;
90+
let lastIdx = 0;
91+
let result = "";
92+
93+
while ((match = regex.exec(data))) {
94+
if (lastIdx !== match.index) {
95+
result += data.substring(lastIdx, match.index);
96+
}
97+
98+
// We know that this chararcter will be in the map.
99+
result += map.get(match[0].charCodeAt(0))!;
100+
101+
// Every match will be of length 1
102+
lastIdx = match.index + 1;
103+
}
104+
105+
return result + data.substring(lastIdx);
106+
};
107+
}
108+
96109
/**
97110
* Encodes all characters not valid in XML documents using XML entities.
98111
*
99112
* Note that the output will be character-set dependent.
100113
*
101114
* @param data String to escape.
102115
*/
103-
export function escapeUTF8(data: string): string {
104-
let match;
105-
let lastIdx = 0;
106-
let result = "";
107-
108-
while ((match = xmlInvalidChars.exec(data))) {
109-
if (lastIdx !== match.index) {
110-
result += data.substring(lastIdx, match.index);
111-
}
112-
113-
// We know that this chararcter will be in `inverseXML`
114-
result += xmlCodeMap.get(match[0].charCodeAt(0))!;
115-
116-
// Every match will be of length 1
117-
lastIdx = match.index + 1;
118-
}
119-
120-
return result + data.substring(lastIdx);
121-
}
116+
export const escapeUTF8 = getEscaper(/[&<>'"]/g, xmlCodeMap);
122117

123118
/**
124119
* Encodes all characters that have to be escaped in HTML attributes,
125120
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
126121
*
127122
* @param data String to escape.
128123
*/
129-
export function escapeAttribute(data: string): string {
130-
return data.replace(
131-
attrReplacer,
132-
(match) => htmlEscapeCodeMap.get(match.charCodeAt(0))!
133-
);
134-
}
124+
export const escapeAttribute = getEscaper(
125+
/["&\u00A0]/g,
126+
new Map([
127+
[34, "&quot;"],
128+
[38, "&amp;"],
129+
[160, "&nbsp;"],
130+
])
131+
);
135132

136133
/**
137134
* Encodes all characters that have to be escaped in HTML text,
138135
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
139136
*
140137
* @param data String to escape.
141138
*/
142-
export function escapeText(data: string): string {
143-
return data.replace(
144-
textReplacer,
145-
(match) => htmlEscapeCodeMap.get(match.charCodeAt(0))!
146-
);
147-
}
139+
export const escapeText = getEscaper(
140+
/[&<>\u00A0]/g,
141+
new Map([
142+
[38, "&amp;"],
143+
[60, "&lt;"],
144+
[62, "&gt;"],
145+
[160, "&nbsp;"],
146+
])
147+
);

0 commit comments

Comments
 (0)