speedy-entities 3.0.1-next.bb17de5 → 3.1.0
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/README.md +21 -22
- package/createEntityDecoder.d.ts +13 -2
- package/createEntityDecoder.js +47 -18
- package/decodeHTML.js +2 -4
- package/decodeXML.js +2 -4
- package/html-entities.d.ts +2234 -0
- package/html-entities.js +2233 -0
- package/index.d.ts +5 -5
- package/index.js +5 -5
- package/package.json +3 -1
- package/xml-entities.d.ts +8 -0
- package/xml-entities.js +7 -0
- package/gen/html-entities.d.ts +0 -2
- package/gen/html-entities.js +0 -1
- package/gen/xml-entities.d.ts +0 -2
- package/gen/xml-entities.js +0 -1
- package/utils.d.ts +0 -1
- package/utils.js +0 -7
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# speedy-entities
|
|
1
|
+
# speedy-entities 🏎💨
|
|
2
2
|
|
|
3
3
|
[The fastest](#performance) XML/HTML entity encoder/decoder in
|
|
4
|
-
[
|
|
4
|
+
[13 kB gzipped](https://bundlephobia.com/package/speedy-entities).
|
|
5
5
|
|
|
6
6
|
```shell
|
|
7
7
|
npm install --save-prod speedy-entities
|
|
@@ -26,25 +26,17 @@ decodeHTML('⪢̸∳');
|
|
|
26
26
|
|
|
27
27
|
## Custom decoder
|
|
28
28
|
|
|
29
|
-
You can create a decoder that decodes custom
|
|
29
|
+
You can create a decoder that decodes your custom character entity references:
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
|
-
import { arrayTrieEncode, trieCreate, trieSet } from '@smikhalevski/trie';
|
|
33
32
|
import { createEntityDecoder } from 'speedy-entities';
|
|
34
33
|
|
|
35
|
-
// Create a trie that would hold entities
|
|
36
|
-
const trie = trieCreate<string>();
|
|
37
|
-
|
|
38
|
-
// Register named entities
|
|
39
|
-
trieSet('foo;', 'okay');
|
|
40
|
-
trieSet('qux;', 'yeah');
|
|
41
|
-
|
|
42
|
-
// Encode a trie
|
|
43
|
-
const entitiesTrie = arrayTrieEncode(trie);
|
|
44
|
-
|
|
45
34
|
// Create a decoder
|
|
46
35
|
const decode = createEntityDecoder({
|
|
47
|
-
|
|
36
|
+
entities: {
|
|
37
|
+
'&foo;': 'okay',
|
|
38
|
+
'&qux;': 'yeah',
|
|
39
|
+
},
|
|
48
40
|
isNumericReferenceSemicolonRequired: true,
|
|
49
41
|
});
|
|
50
42
|
|
|
@@ -78,14 +70,21 @@ escapeXML('&😘❤️');
|
|
|
78
70
|
|
|
79
71
|
# Performance
|
|
80
72
|
|
|
81
|
-
|
|
73
|
+
Results are in millions of operations per second (MHz). The higher number is better.
|
|
82
74
|
|
|
83
|
-
|
|
75
|
+
| Library | Decode HTML | Decode XML | Encode XML | Escape XML |
|
|
76
|
+
| --------------------------------: | ----------: | ---------: | ---------: | ---------: |
|
|
77
|
+
| **speedy-entities**​@3.1.0 | **4.9** | **5.2** | **5.3** | **7.2** |
|
|
78
|
+
| **entities**​@6.0.1 | 3.4 | 3.6 | 4.5 | 6.1 |
|
|
79
|
+
| **html-entities**​@2.6.0 | 2.2 | 2.3 | 3.3 | 5.1 |
|
|
80
|
+
| **he**​@1.2.0 | 1.9 | 1.8 | 2.2 | 5.2 |
|
|
84
81
|
|
|
85
|
-
|
|
82
|
+
Tests were conducted using [TooFast](https://github.com/smikhalevski/toofast#readme) on Apple M1 with Node.js v23.11.1.
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
To reproduce [the performance test suite](./src/test/perf/overall.perf.js) results, clone this repo and run:
|
|
88
85
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
```shell
|
|
87
|
+
npm ci
|
|
88
|
+
npm run build
|
|
89
|
+
npm run perf
|
|
90
|
+
```
|
package/createEntityDecoder.d.ts
CHANGED
|
@@ -2,8 +2,19 @@
|
|
|
2
2
|
* The options recognized by {@linkcode createEntityDecoder}.
|
|
3
3
|
*/
|
|
4
4
|
export interface EntityDecoderOptions {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Mapping from an [character entity reference](https://www.w3.org/TR/html4/charset.html#h-5.3.2) to its decoded value.
|
|
7
|
+
* Entity references should contain only [a-zA-Z] and may optionally start with an ampersand "&" and end with
|
|
8
|
+
* a semicolon ";".
|
|
9
|
+
*
|
|
10
|
+
* ```json
|
|
11
|
+
* {
|
|
12
|
+
* "Δ": "\u0394",
|
|
13
|
+
* "LongRightArrow;": "\u27F6"
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
entities?: Record<string, string>;
|
|
7
18
|
/**
|
|
8
19
|
* If `true` then [numeric character references](https://www.w3.org/TR/html4/charset.html#h-5.3.1) must be terminated
|
|
9
20
|
* with a semicolon to be decoded. Otherwise, numeric character references are recognized even if they aren't
|
package/createEntityDecoder.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const fromCharCode = String.fromCharCode;
|
|
2
1
|
/**
|
|
3
2
|
* Creates an entity decoder that rewrites numeric and named entities found in input to their respective values.
|
|
4
3
|
*
|
|
@@ -6,7 +5,20 @@ const fromCharCode = String.fromCharCode;
|
|
|
6
5
|
* @returns A function that decodes entities in the string.
|
|
7
6
|
*/
|
|
8
7
|
export function createEntityDecoder(options = {}) {
|
|
9
|
-
const { entities,
|
|
8
|
+
const { entities, isNumericReferenceSemicolonRequired = false } = options;
|
|
9
|
+
const fromCharCode = String.fromCharCode;
|
|
10
|
+
let entityDereferenceMap;
|
|
11
|
+
let maximumEntityReferenceLength = 32;
|
|
12
|
+
if (entities !== undefined) {
|
|
13
|
+
entityDereferenceMap = new Map();
|
|
14
|
+
for (const entity in entities) {
|
|
15
|
+
const hashCode = getEntityReferenceHashCode(entity);
|
|
16
|
+
if (hashCode !== 0) {
|
|
17
|
+
maximumEntityReferenceLength = Math.max(maximumEntityReferenceLength, entity.length);
|
|
18
|
+
entityDereferenceMap.set(hashCode, entities[entity]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
10
22
|
return input => {
|
|
11
23
|
let output = '';
|
|
12
24
|
let textIndex = 0;
|
|
@@ -90,23 +102,21 @@ export function createEntityDecoder(options = {}) {
|
|
|
90
102
|
}
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
|
-
else if (
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
105
|
+
else if (entityDereferenceMap !== undefined) {
|
|
106
|
+
// Character entity reference
|
|
107
|
+
let index = startIndex;
|
|
108
|
+
let hashCode = 0;
|
|
109
|
+
let referencedValue;
|
|
110
|
+
while (charCode !== /* ; */ 59 &&
|
|
111
|
+
index < inputLength &&
|
|
112
|
+
index - startIndex < maximumEntityReferenceLength &&
|
|
113
|
+
((charCode = input.charCodeAt(index) | 0), isEntityReferenceChar(charCode))) {
|
|
114
|
+
++index;
|
|
102
115
|
hashCode = (hashCode << 5) - hashCode + charCode;
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
value =
|
|
106
|
-
endIndex = index
|
|
107
|
-
}
|
|
108
|
-
if (charCode === /* ; */ 59 || index - startIndex >= maximumNamedReferenceLength) {
|
|
109
|
-
break;
|
|
116
|
+
referencedValue = entityDereferenceMap.get(hashCode);
|
|
117
|
+
if (referencedValue !== undefined) {
|
|
118
|
+
value = referencedValue;
|
|
119
|
+
endIndex = index;
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
122
|
}
|
|
@@ -151,3 +161,22 @@ const entityOverrides = {
|
|
|
151
161
|
158: '\u017E',
|
|
152
162
|
159: '\u0178',
|
|
153
163
|
};
|
|
164
|
+
function getEntityReferenceHashCode(entity) {
|
|
165
|
+
if (entity.length === 0) {
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
let hashCode = 0;
|
|
169
|
+
for (let index = entity.charCodeAt(0) === /* & */ 38 ? 1 : 0; index < entity.length; ++index) {
|
|
170
|
+
const charCode = entity.charCodeAt(index);
|
|
171
|
+
if (!isEntityReferenceChar(charCode)) {
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
hashCode = (hashCode << 5) - hashCode + entity.charCodeAt(index);
|
|
175
|
+
}
|
|
176
|
+
return hashCode;
|
|
177
|
+
}
|
|
178
|
+
function isEntityReferenceChar(charCode) {
|
|
179
|
+
return ((charCode >= /* a */ 97 && charCode <= /* z */ 122) ||
|
|
180
|
+
(charCode >= /* A */ 65 && charCode <= /* Z */ 90) ||
|
|
181
|
+
charCode === /* ; */ 59);
|
|
182
|
+
}
|
package/decodeHTML.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { createEntityDecoder } from './createEntityDecoder.js';
|
|
2
|
-
import
|
|
3
|
-
import htmlEntities from './gen/html-entities.js';
|
|
2
|
+
import htmlEntities from './html-entities.js';
|
|
4
3
|
/**
|
|
5
4
|
* Decodes all known HTML entities and numeric character references in the input.
|
|
6
5
|
*/
|
|
7
6
|
export const decodeHTML = createEntityDecoder({
|
|
8
|
-
entities:
|
|
9
|
-
maximumNamedReferenceLength: 32,
|
|
7
|
+
entities: htmlEntities,
|
|
10
8
|
isNumericReferenceSemicolonRequired: false,
|
|
11
9
|
});
|
package/decodeXML.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { createEntityDecoder } from './createEntityDecoder.js';
|
|
2
|
-
import
|
|
3
|
-
import xmlEntities from './gen/xml-entities.js';
|
|
2
|
+
import xmlEntities from './xml-entities.js';
|
|
4
3
|
/**
|
|
5
4
|
* Decodes XML entities and numeric character references in the input.
|
|
6
5
|
*/
|
|
7
6
|
export const decodeXML = createEntityDecoder({
|
|
8
|
-
entities:
|
|
9
|
-
maximumNamedReferenceLength: 5,
|
|
7
|
+
entities: xmlEntities,
|
|
10
8
|
isNumericReferenceSemicolonRequired: true,
|
|
11
9
|
});
|