ghtml 1.2.2 → 1.2.4
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 +18 -11
- package/bench/index.js +2 -2
- package/package.json +3 -2
- package/src/html.js +18 -12
- package/test/index.js +45 -11
package/README.md
CHANGED
|
@@ -12,11 +12,17 @@ npm i ghtml
|
|
|
12
12
|
|
|
13
13
|
### `html`
|
|
14
14
|
|
|
15
|
-
The `html` function is
|
|
15
|
+
The `html` function is designed to tag template literals and automatically escape their expressions to prevent XSS attacks. To intentionally bypass escaping for a specific expression, prefix it with `!`.
|
|
16
16
|
|
|
17
17
|
### `htmlGenerator`
|
|
18
18
|
|
|
19
|
-
The `htmlGenerator` function
|
|
19
|
+
The `htmlGenerator` function acts as the generator version of the `html` function. It facilitates the creation of HTML fragments iteratively, making it ideal for parsing large templates or constructing HTML content dynamically.
|
|
20
|
+
|
|
21
|
+
**Note:**
|
|
22
|
+
|
|
23
|
+
A key difference of `htmlGenerator` is its ability to recognize and properly handle iterable elements within array expressions. This is to detect nested `htmlGenerator` usage, enabling scenarios such as ``[1,2,3].map(i => htmlGenerator`<li>${i}</li>`)``.
|
|
24
|
+
|
|
25
|
+
As a side effect, an expression like `${[[1, 2, 3], 4]}` (where an element is an array itself) will not be rendered as `"1,2,34"`, which is the case with `html`, but as `"1234"`. This is the intended behavior most of the time anyway.
|
|
20
26
|
|
|
21
27
|
### `includeFile`
|
|
22
28
|
|
|
@@ -77,15 +83,16 @@ const htmlString = html`
|
|
|
77
83
|
import { htmlGenerator as html } from "ghtml";
|
|
78
84
|
import { Readable } from "node:stream";
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
})
|
|
86
|
+
http
|
|
87
|
+
.createServer((req, res) => {
|
|
88
|
+
const htmlContent = htmlGenerator`<html>
|
|
89
|
+
<p>${"...HTML content..."}</p>
|
|
90
|
+
</html>`;
|
|
91
|
+
const readableStream = Readable.from(htmlContent);
|
|
92
|
+
res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
|
|
93
|
+
readableStream.pipe(res);
|
|
94
|
+
})
|
|
95
|
+
.listen(3000);
|
|
89
96
|
```
|
|
90
97
|
|
|
91
98
|
### `includeFile`
|
package/bench/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-unused-expressions */
|
|
2
|
-
import { html } from "../src/
|
|
2
|
+
import { html } from "../src/index.js";
|
|
3
3
|
import { Bench } from "tinybench";
|
|
4
4
|
import { writeFileSync } from "node:fs";
|
|
5
5
|
import { Buffer } from "node:buffer";
|
|
@@ -64,7 +64,7 @@ bench.add("Large strings", () => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
bench.add("High iteration count", () => {
|
|
67
|
-
for (let i = 0; i
|
|
67
|
+
for (let i = 0; i !== 1000; i++) {
|
|
68
68
|
html`<span>${i}</span>`;
|
|
69
69
|
}
|
|
70
70
|
});
|
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": "1.2.
|
|
6
|
+
"version": "1.2.4",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.js",
|
|
9
9
|
"exports": {
|
|
@@ -14,12 +14,13 @@
|
|
|
14
14
|
"node": ">=18"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "npm run lint && node --
|
|
17
|
+
"test": "npm run lint && c8 --100 node --test test/**",
|
|
18
18
|
"lint": "eslint . && prettier --check .",
|
|
19
19
|
"lint:fix": "eslint --fix . && prettier --write ."
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@fastify/pre-commit": "^2.1.0",
|
|
23
|
+
"c8": "^9.1.0",
|
|
23
24
|
"grules": "^0.15.0",
|
|
24
25
|
"tinybench": "^2.6.0"
|
|
25
26
|
},
|
package/src/html.js
CHANGED
|
@@ -24,7 +24,7 @@ const html = ({ raw: literals }, ...expressions) => {
|
|
|
24
24
|
let accumulator = "";
|
|
25
25
|
let index = 0;
|
|
26
26
|
|
|
27
|
-
for (; index
|
|
27
|
+
for (; index !== expressions.length; ++index) {
|
|
28
28
|
let literal = literals[index];
|
|
29
29
|
let expression =
|
|
30
30
|
typeof expressions[index] === "string"
|
|
@@ -55,7 +55,7 @@ const html = ({ raw: literals }, ...expressions) => {
|
|
|
55
55
|
const htmlGenerator = function* ({ raw: literals }, ...expressions) {
|
|
56
56
|
let index = 0;
|
|
57
57
|
|
|
58
|
-
for (; index
|
|
58
|
+
for (; index !== expressions.length; ++index) {
|
|
59
59
|
let literal = literals[index];
|
|
60
60
|
let expression;
|
|
61
61
|
|
|
@@ -63,8 +63,6 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
|
|
|
63
63
|
expression = expressions[index];
|
|
64
64
|
} else if (expressions[index] == null) {
|
|
65
65
|
expression = "";
|
|
66
|
-
} else if (Array.isArray(expressions[index])) {
|
|
67
|
-
expression = expressions[index].join("");
|
|
68
66
|
} else {
|
|
69
67
|
if (typeof expressions[index][Symbol.iterator] === "function") {
|
|
70
68
|
const isRaw =
|
|
@@ -79,14 +77,22 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
|
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
for (const value of expressions[index]) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
if (typeof value === "string") {
|
|
81
|
+
expression = value;
|
|
82
|
+
} else if (value == null) {
|
|
83
|
+
expression = "";
|
|
84
|
+
} else if (typeof value[Symbol.iterator] === "function") {
|
|
85
|
+
expression = "";
|
|
86
|
+
|
|
87
|
+
for (const innerValue of value) {
|
|
88
|
+
if (innerValue != null) {
|
|
89
|
+
// At this level, we simply mirror Array.prototype.join
|
|
90
|
+
expression += innerValue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
expression = `${value}`;
|
|
95
|
+
}
|
|
90
96
|
|
|
91
97
|
if (expression.length) {
|
|
92
98
|
if (!isRaw) {
|
package/test/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { html, htmlGenerator } from "../src/index.js";
|
|
1
2
|
import test from "node:test";
|
|
2
3
|
import assert from "node:assert";
|
|
3
|
-
import { html, htmlGenerator } from "../src/html.js";
|
|
4
4
|
|
|
5
5
|
const conditionTrue = true;
|
|
6
6
|
const conditionFalse = false;
|
|
@@ -134,18 +134,30 @@ test("htmlGenerator renders safe content", () => {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
test("htmlGenerator renders unsafe content", () => {
|
|
137
|
-
const generator = htmlGenerator`<p>${
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
const generator = htmlGenerator`<p>${descriptionSafe}${descriptionUnsafe}${htmlGenerator`${array1}`}${null}${255}</p>`;
|
|
138
|
+
let accumulator = "";
|
|
139
|
+
|
|
140
|
+
for (const value of generator) {
|
|
141
|
+
accumulator += value;
|
|
142
|
+
}
|
|
143
|
+
|
|
142
144
|
assert.strictEqual(
|
|
143
|
-
|
|
144
|
-
"
|
|
145
|
+
accumulator,
|
|
146
|
+
"<p>This is a safe description.<script>alert('This is an unsafe description.')</script>12345255</p>",
|
|
145
147
|
);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("htmlGenerator works with nested htmlGenerator calls in an array", () => {
|
|
151
|
+
const generator = htmlGenerator`<ul>!${[1, 2, 3].map((index) => {
|
|
152
|
+
return htmlGenerator`<li>${index}</li>`;
|
|
153
|
+
})}</ul>`;
|
|
154
|
+
let accumulator = "";
|
|
155
|
+
|
|
156
|
+
for (const value of generator) {
|
|
157
|
+
accumulator += value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
assert.strictEqual(accumulator, "<ul><li>1</li><li>2</li><li>3</li></ul>");
|
|
149
161
|
assert.strictEqual(generator.next().done, true);
|
|
150
162
|
});
|
|
151
163
|
|
|
@@ -164,3 +176,25 @@ test("htmlGenerator works with other generators", () => {
|
|
|
164
176
|
assert.strictEqual(generator.next().value, "</div>");
|
|
165
177
|
assert.strictEqual(generator.next().done, true);
|
|
166
178
|
});
|
|
179
|
+
|
|
180
|
+
test("htmlGenerator works with other generators within an array (raw)", () => {
|
|
181
|
+
const generator = htmlGenerator`<div>!${[generatorExample()]}</div>`;
|
|
182
|
+
assert.strictEqual(generator.next().value, "<div>");
|
|
183
|
+
assert.strictEqual(
|
|
184
|
+
generator.next().value,
|
|
185
|
+
"<p>This is a safe description.<script>alert('This is an unsafe description.')</script>1,2,3,4,5255</p>",
|
|
186
|
+
);
|
|
187
|
+
assert.strictEqual(generator.next().value, "</div>");
|
|
188
|
+
assert.strictEqual(generator.next().done, true);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("htmlGenerator works with other generators within an array (escaped)", () => {
|
|
192
|
+
const generator = htmlGenerator`<div>${[generatorExample()]}</div>`;
|
|
193
|
+
assert.strictEqual(generator.next().value, "<div>");
|
|
194
|
+
assert.strictEqual(
|
|
195
|
+
generator.next().value,
|
|
196
|
+
"<p>This is a safe description.<script>alert('This is an unsafe description.')</script>1,2,3,4,5255</p>",
|
|
197
|
+
);
|
|
198
|
+
assert.strictEqual(generator.next().value, "</div>");
|
|
199
|
+
assert.strictEqual(generator.next().done, true);
|
|
200
|
+
});
|