ghtml 2.3.0 → 3.0.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 +45 -5
- package/SECURITY.md +2 -1
- package/bin/src/index.js +1 -1
- package/bin/src/utils.js +1 -1
- package/package.json +2 -2
- package/src/html.js +11 -12
- package/test/index.js +9 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
# ghtml 
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ta**ghtml** lets you replace your template engine with fast JavaScript by leveraging the power of tagged templates.
|
|
4
|
+
|
|
5
|
+
Works in the browser. No runtime dependencies. [~30x faster than React. ~10x faster than common-tags.](#benchmarks)
|
|
4
6
|
|
|
5
7
|

|
|
6
8
|
|
|
@@ -10,6 +12,12 @@ Inspired by [html-template-tag](https://github.com/AntonioVdlC/html-template-tag
|
|
|
10
12
|
npm i ghtml
|
|
11
13
|
```
|
|
12
14
|
|
|
15
|
+
Or import directly from a CDN:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { html } from "https://cdn.jsdelivr.net/npm/ghtml/+esm";
|
|
19
|
+
```
|
|
20
|
+
|
|
13
21
|
## API
|
|
14
22
|
|
|
15
23
|
### `html`
|
|
@@ -34,7 +42,7 @@ Because they return generators instead of strings, a key difference of `htmlGene
|
|
|
34
42
|
|
|
35
43
|
### `includeFile`
|
|
36
44
|
|
|
37
|
-
Available in Node.js, the `includeFile` function is a wrapper around `readFileSync`. It reads and returns the content of a file while
|
|
45
|
+
Available in Node.js, the `includeFile` function is a wrapper around `readFileSync`. It reads and returns the content of a file while caching it in memory for faster future reuse.
|
|
38
46
|
|
|
39
47
|
## Usage
|
|
40
48
|
|
|
@@ -47,7 +55,7 @@ const username = '<img src="https://example.com/pwned.png">';
|
|
|
47
55
|
const greeting = html`<h1>Hello, ${username}</h1>`;
|
|
48
56
|
|
|
49
57
|
console.log(greeting);
|
|
50
|
-
// Output: <h1>Hello, <img src
|
|
58
|
+
// Output: <h1>Hello, <img src="https://example.com/pwned.png"></h1>
|
|
51
59
|
```
|
|
52
60
|
|
|
53
61
|
To bypass escaping:
|
|
@@ -171,6 +179,38 @@ console.log(logo);
|
|
|
171
179
|
// Output: content of "static/logo.svg"
|
|
172
180
|
```
|
|
173
181
|
|
|
182
|
+
## Benchmarks
|
|
183
|
+
|
|
184
|
+
Latest results [from Kita](https://github.com/kitajs/html/tree/cb7950c68489ff70dd0b0c130c9b70046c1543ea/benchmarks):
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
benchmark time (avg) (min … max) p75 p99 p999
|
|
188
|
+
--------------------------------------------------- -----------------------------
|
|
189
|
+
• Real World Scenario
|
|
190
|
+
--------------------------------------------------- -----------------------------
|
|
191
|
+
KitaJS/Html 505 µs/iter (387 µs … 2'007 µs) 417 µs 1'209 µs 1'857 µs
|
|
192
|
+
Typed Html 1'844 µs/iter (1'604 µs … 2'415 µs) 2'088 µs 2'211 µs 2'415 µs
|
|
193
|
+
VHtml 2'424 µs/iter (2'250 µs … 2'864 µs) 2'462 µs 2'829 µs 2'864 µs
|
|
194
|
+
React JSX 6'416 µs/iter (5'893 µs … 9'399 µs) 6'840 µs 9'399 µs 9'399 µs
|
|
195
|
+
Preact 970 µs/iter (673 µs … 5'038 µs) 766 µs 2'224 µs 5'038 µs
|
|
196
|
+
React 6'319 µs/iter (5'885 µs … 7'306 µs) 6'678 µs 7'306 µs 7'306 µs
|
|
197
|
+
Common Tags 2'967 µs/iter (2'774 µs … 3'801 µs) 2'916 µs 3'794 µs 3'801 µs
|
|
198
|
+
Ghtml 225 µs/iter (184 µs … 1'567 µs) 206 µs 1'066 µs 1'450 µs
|
|
199
|
+
JSXTE 4'489 µs/iter (3'605 µs … 6'215 µs) 4'517 µs 6'062 µs 6'215 µs
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
summary for Real World Scenario
|
|
203
|
+
Ghtml
|
|
204
|
+
2.25x faster than KitaJS/Html
|
|
205
|
+
4.32x faster than Preact
|
|
206
|
+
8.21x faster than Typed Html
|
|
207
|
+
10.8x faster than VHtml
|
|
208
|
+
13.22x faster than Common Tags
|
|
209
|
+
20x faster than JSXTE
|
|
210
|
+
28.15x faster than React
|
|
211
|
+
28.58x faster than React JSX
|
|
212
|
+
```
|
|
213
|
+
|
|
174
214
|
## Security
|
|
175
215
|
|
|
176
|
-
Like [similar](https://github.com/mde/ejs/blob/
|
|
216
|
+
Like [similar tools](https://github.com/mde/ejs/blob/a4770b8ff49b93387c7f2760d957446cd332531a/SECURITY.md#out-of-scope-vulnerabilities), ghtml does not prevent all kinds of XSS attacks. It is the responsibility of developers to sanitize user inputs. Some inherently insecure uses include dynamically generating JavaScript, failing to quote HTML attribute values, and relying on unsanitized user-provided URIs.
|
package/SECURITY.md
CHANGED
package/bin/src/index.js
CHANGED
|
@@ -19,7 +19,7 @@ const parseArguments = (args) => {
|
|
|
19
19
|
|
|
20
20
|
if (!roots || !refs) {
|
|
21
21
|
console.error(
|
|
22
|
-
'Usage: npx ghtml --roots="path/to/scan/
|
|
22
|
+
'Usage: npx ghtml --roots="base/path/to/scan/assets/1/,base/path/to/scan/assets/2/" --refs="views/path/to/append/hashes/1/,views/path/to/append/hashes/2/" [--prefix="/optional/prefix/"]',
|
|
23
23
|
);
|
|
24
24
|
process.exit(1);
|
|
25
25
|
}
|
package/bin/src/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Replace your template engine with fast JavaScript by leveraging the power of tagged templates.",
|
|
4
4
|
"author": "Gürgün Dayıoğlu",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "3.0.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": "./bin/src/index.js",
|
|
9
9
|
"main": "./src/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@fastify/pre-commit": "^2.1.0",
|
|
28
28
|
"c8": "^10.1.2",
|
|
29
|
-
"grules": "^0.
|
|
29
|
+
"grules": "^0.23.0",
|
|
30
30
|
"tinybench": "^2.8.0"
|
|
31
31
|
},
|
|
32
32
|
"repository": {
|
package/src/html.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const escapeRegExp = /["&'
|
|
1
|
+
const escapeRegExp = /["&'<=>]/;
|
|
2
2
|
|
|
3
3
|
const escapeFunction = (string) => {
|
|
4
4
|
let escaped = "";
|
|
@@ -22,12 +22,12 @@ const escapeFunction = (string) => {
|
|
|
22
22
|
escaped += string.slice(start, end) + "<";
|
|
23
23
|
start = end + 1;
|
|
24
24
|
continue;
|
|
25
|
-
case
|
|
26
|
-
escaped += string.slice(start, end) + "&#
|
|
25
|
+
case 61: // =
|
|
26
|
+
escaped += string.slice(start, end) + "=";
|
|
27
27
|
start = end + 1;
|
|
28
28
|
continue;
|
|
29
|
-
case
|
|
30
|
-
escaped += string.slice(start, end) + "&#
|
|
29
|
+
case 62: // >
|
|
30
|
+
escaped += string.slice(start, end) + ">";
|
|
31
31
|
start = end + 1;
|
|
32
32
|
continue;
|
|
33
33
|
}
|
|
@@ -47,16 +47,15 @@ const html = ({ raw: literals }, ...expressions) => {
|
|
|
47
47
|
let accumulator = "";
|
|
48
48
|
|
|
49
49
|
for (let i = 0; i !== expressions.length; ++i) {
|
|
50
|
-
const expression = expressions[i];
|
|
51
50
|
let literal = literals[i];
|
|
52
51
|
let string =
|
|
53
|
-
typeof
|
|
54
|
-
?
|
|
55
|
-
:
|
|
52
|
+
typeof expressions[i] === "string"
|
|
53
|
+
? expressions[i]
|
|
54
|
+
: expressions[i] == null
|
|
56
55
|
? ""
|
|
57
|
-
: Array.isArray(
|
|
58
|
-
?
|
|
59
|
-
: `${
|
|
56
|
+
: Array.isArray(expressions[i])
|
|
57
|
+
? expressions[i].join("")
|
|
58
|
+
: `${expressions[i]}`;
|
|
60
59
|
|
|
61
60
|
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
62
61
|
literal = literal.slice(0, -1);
|
package/test/index.js
CHANGED
|
@@ -66,7 +66,15 @@ test("renders unsafe content", () => {
|
|
|
66
66
|
test("renders unsafe content /2", () => {
|
|
67
67
|
assert.strictEqual(
|
|
68
68
|
html`<p>${`${descriptionUnsafe}"&\``}</p>`,
|
|
69
|
-
`<p><script>alert('This is an unsafe description.')</script>"&
|
|
69
|
+
`<p><script>alert('This is an unsafe description.')</script>"&\`</p>`,
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("renders unsafe content /3", () => {
|
|
74
|
+
// prettier-ignore
|
|
75
|
+
assert.strictEqual(
|
|
76
|
+
html`<img src="https://picsum.photos/200/300" alt=${"altText onload=alert(String.fromCharCode(112,119,110,101,100))"} />`,
|
|
77
|
+
`<img src="https://picsum.photos/200/300" alt=altText onload=alert(String.fromCharCode(112,119,110,101,100)) />`,
|
|
70
78
|
);
|
|
71
79
|
});
|
|
72
80
|
|