ghtml 1.6.0 → 1.7.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 +8 -7
- package/package.json +1 -1
- package/src/html.js +48 -34
- package/test/index.js +44 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Inspired by [html-template-tag](https://github.com/AntonioVdlC/html-template-tag
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```sh
|
|
8
8
|
npm i ghtml
|
|
9
9
|
```
|
|
10
10
|
|
|
@@ -24,9 +24,9 @@ Keep in mind that, in Node.js, all else being equal, streaming a response using
|
|
|
24
24
|
|
|
25
25
|
### `htmlAsyncGenerator`
|
|
26
26
|
|
|
27
|
-
This version of HTML generator should be preferred for asynchronous use cases. The output will be generated as the promise expressions resolve.
|
|
27
|
+
This version of HTML generator should be preferred for asynchronous and streaming use cases. The output will be generated as the promise expressions resolve or stream expressions send data.
|
|
28
28
|
|
|
29
|
-
**Note:**
|
|
29
|
+
**Minor Note:**
|
|
30
30
|
|
|
31
31
|
Because they return generators instead of strings, a key difference of `htmlGenerator` and `htmlAsyncGenerator` is their ability to recognize and properly handle iterable elements within array expressions. This is to detect nested `htmlGenerator` and `htmlAsyncGenerator` usage, enabling scenarios such as ``${[1, 2, 3].map(i => htmlGenerator`<li>${i}</li>`)}``.
|
|
32
32
|
|
|
@@ -134,11 +134,12 @@ import { Readable } from "node:stream";
|
|
|
134
134
|
import http from "node:http";
|
|
135
135
|
|
|
136
136
|
const asyncGenerator = async function* () {
|
|
137
|
-
const helloWorld =
|
|
137
|
+
const helloWorld = new Promise((resolve) => {
|
|
138
138
|
setTimeout(() => {
|
|
139
|
-
resolve("Hello, World!");
|
|
140
|
-
},
|
|
139
|
+
resolve("<br /><br />Hello, World!");
|
|
140
|
+
}, 2500);
|
|
141
141
|
});
|
|
142
|
+
yield await readFile("./.gitignore", "utf8");
|
|
142
143
|
yield helloWorld;
|
|
143
144
|
};
|
|
144
145
|
|
|
@@ -146,7 +147,7 @@ http
|
|
|
146
147
|
.createServer((req, res) => {
|
|
147
148
|
const htmlContent = html`<!doctype html>
|
|
148
149
|
<html>
|
|
149
|
-
<p
|
|
150
|
+
<p>!${asyncGenerator()}</p>
|
|
150
151
|
<code>${readFile("./README.md", "utf8")}</code>
|
|
151
152
|
<code>${createReadStream("./README.md", "utf8")}</code>
|
|
152
153
|
</html>`;
|
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.
|
|
6
|
+
"version": "1.7.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.js",
|
|
9
9
|
"exports": {
|
package/src/html.js
CHANGED
|
@@ -8,11 +8,25 @@ const escapeDictionary = {
|
|
|
8
8
|
|
|
9
9
|
const escapeRegExp = new RegExp(
|
|
10
10
|
`[${Object.keys(escapeDictionary).join("")}]`,
|
|
11
|
-
"
|
|
11
|
+
"u",
|
|
12
12
|
);
|
|
13
13
|
|
|
14
|
-
const escapeFunction = (
|
|
15
|
-
|
|
14
|
+
const escapeFunction = (string) => {
|
|
15
|
+
const stringLength = string.length;
|
|
16
|
+
let start = 0;
|
|
17
|
+
let end = 0;
|
|
18
|
+
let escaped = "";
|
|
19
|
+
|
|
20
|
+
do {
|
|
21
|
+
const escapedCharacter = escapeDictionary[string[end++]];
|
|
22
|
+
|
|
23
|
+
if (escapedCharacter) {
|
|
24
|
+
escaped += string.slice(start, end - 1) + escapedCharacter;
|
|
25
|
+
start = end;
|
|
26
|
+
}
|
|
27
|
+
} while (end !== stringLength);
|
|
28
|
+
|
|
29
|
+
return escaped + string.slice(start, end);
|
|
16
30
|
};
|
|
17
31
|
|
|
18
32
|
const arrayIsArray = Array.isArray;
|
|
@@ -23,8 +37,8 @@ const arrayIsArray = Array.isArray;
|
|
|
23
37
|
* @returns {string} The HTML string.
|
|
24
38
|
*/
|
|
25
39
|
const html = ({ raw: literals }, ...expressions) => {
|
|
26
|
-
let accumulator = "";
|
|
27
40
|
let index = 0;
|
|
41
|
+
let accumulator = "";
|
|
28
42
|
|
|
29
43
|
for (; index !== expressions.length; ++index) {
|
|
30
44
|
const expression = expressions[index];
|
|
@@ -38,16 +52,16 @@ const html = ({ raw: literals }, ...expressions) => {
|
|
|
38
52
|
? expression.join("")
|
|
39
53
|
: `${expression}`;
|
|
40
54
|
|
|
41
|
-
if (literal
|
|
55
|
+
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
42
56
|
literal = literal.slice(0, -1);
|
|
43
|
-
} else if (string.
|
|
44
|
-
string = string
|
|
57
|
+
} else if (string && escapeRegExp.test(string)) {
|
|
58
|
+
string = escapeFunction(string);
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
accumulator += literal + string;
|
|
48
62
|
}
|
|
49
63
|
|
|
50
|
-
return
|
|
64
|
+
return accumulator + literals[index];
|
|
51
65
|
};
|
|
52
66
|
|
|
53
67
|
/**
|
|
@@ -70,13 +84,13 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
|
|
|
70
84
|
} else {
|
|
71
85
|
if (expression[Symbol.iterator]) {
|
|
72
86
|
const isRaw =
|
|
73
|
-
literal
|
|
87
|
+
literal !== "" && literal.charCodeAt(literal.length - 1) === 33;
|
|
74
88
|
|
|
75
89
|
if (isRaw) {
|
|
76
90
|
literal = literal.slice(0, -1);
|
|
77
91
|
}
|
|
78
92
|
|
|
79
|
-
if (literal
|
|
93
|
+
if (literal) {
|
|
80
94
|
yield literal;
|
|
81
95
|
}
|
|
82
96
|
|
|
@@ -96,9 +110,9 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
|
|
|
96
110
|
|
|
97
111
|
string = `${expression}`;
|
|
98
112
|
|
|
99
|
-
if (string
|
|
100
|
-
if (!isRaw) {
|
|
101
|
-
string = string
|
|
113
|
+
if (string) {
|
|
114
|
+
if (!isRaw && escapeRegExp.test(string)) {
|
|
115
|
+
string = escapeFunction(string);
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
yield string;
|
|
@@ -111,9 +125,9 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
|
|
|
111
125
|
string = `${expression}`;
|
|
112
126
|
}
|
|
113
127
|
|
|
114
|
-
if (string
|
|
115
|
-
if (!isRaw) {
|
|
116
|
-
string = string
|
|
128
|
+
if (string) {
|
|
129
|
+
if (!isRaw && escapeRegExp.test(string)) {
|
|
130
|
+
string = escapeFunction(string);
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
yield string;
|
|
@@ -126,18 +140,18 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
|
|
|
126
140
|
string = `${expression}`;
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
if (literal
|
|
143
|
+
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
130
144
|
literal = literal.slice(0, -1);
|
|
131
|
-
} else if (string.
|
|
132
|
-
string = string
|
|
145
|
+
} else if (string && escapeRegExp.test(string)) {
|
|
146
|
+
string = escapeFunction(string);
|
|
133
147
|
}
|
|
134
148
|
|
|
135
|
-
if (literal
|
|
149
|
+
if (literal || string) {
|
|
136
150
|
yield literal + string;
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
153
|
|
|
140
|
-
if (literals[index]
|
|
154
|
+
if (literals[index]) {
|
|
141
155
|
yield literals[index];
|
|
142
156
|
}
|
|
143
157
|
};
|
|
@@ -162,13 +176,13 @@ const htmlAsyncGenerator = async function* ({ raw: literals }, ...expressions) {
|
|
|
162
176
|
} else {
|
|
163
177
|
if (expression[Symbol.iterator] || expression[Symbol.asyncIterator]) {
|
|
164
178
|
const isRaw =
|
|
165
|
-
literal
|
|
179
|
+
literal !== "" && literal.charCodeAt(literal.length - 1) === 33;
|
|
166
180
|
|
|
167
181
|
if (isRaw) {
|
|
168
182
|
literal = literal.slice(0, -1);
|
|
169
183
|
}
|
|
170
184
|
|
|
171
|
-
if (literal
|
|
185
|
+
if (literal) {
|
|
172
186
|
yield literal;
|
|
173
187
|
}
|
|
174
188
|
|
|
@@ -191,9 +205,9 @@ const htmlAsyncGenerator = async function* ({ raw: literals }, ...expressions) {
|
|
|
191
205
|
|
|
192
206
|
string = `${expression}`;
|
|
193
207
|
|
|
194
|
-
if (string
|
|
195
|
-
if (!isRaw) {
|
|
196
|
-
string = string
|
|
208
|
+
if (string) {
|
|
209
|
+
if (!isRaw && escapeRegExp.test(string)) {
|
|
210
|
+
string = escapeFunction(string);
|
|
197
211
|
}
|
|
198
212
|
|
|
199
213
|
yield string;
|
|
@@ -206,9 +220,9 @@ const htmlAsyncGenerator = async function* ({ raw: literals }, ...expressions) {
|
|
|
206
220
|
string = `${expression}`;
|
|
207
221
|
}
|
|
208
222
|
|
|
209
|
-
if (string
|
|
210
|
-
if (!isRaw) {
|
|
211
|
-
string = string
|
|
223
|
+
if (string) {
|
|
224
|
+
if (!isRaw && escapeRegExp.test(string)) {
|
|
225
|
+
string = escapeFunction(string);
|
|
212
226
|
}
|
|
213
227
|
|
|
214
228
|
yield string;
|
|
@@ -221,18 +235,18 @@ const htmlAsyncGenerator = async function* ({ raw: literals }, ...expressions) {
|
|
|
221
235
|
string = `${expression}`;
|
|
222
236
|
}
|
|
223
237
|
|
|
224
|
-
if (literal
|
|
238
|
+
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
225
239
|
literal = literal.slice(0, -1);
|
|
226
|
-
} else if (string.
|
|
227
|
-
string = string
|
|
240
|
+
} else if (string && escapeRegExp.test(string)) {
|
|
241
|
+
string = escapeFunction(string);
|
|
228
242
|
}
|
|
229
243
|
|
|
230
|
-
if (literal
|
|
244
|
+
if (literal || string) {
|
|
231
245
|
yield literal + string;
|
|
232
246
|
}
|
|
233
247
|
}
|
|
234
248
|
|
|
235
|
-
if (literals[index]
|
|
249
|
+
if (literals[index]) {
|
|
236
250
|
yield literals[index];
|
|
237
251
|
}
|
|
238
252
|
};
|
package/test/index.js
CHANGED
|
@@ -174,7 +174,7 @@ test("htmlGenerator works with nested htmlGenerator calls in an array", () => {
|
|
|
174
174
|
assert.strictEqual(generator.next().done, true);
|
|
175
175
|
});
|
|
176
176
|
|
|
177
|
-
test("htmlGenerator works with other generators", () => {
|
|
177
|
+
test("htmlGenerator works with other generators (raw)", () => {
|
|
178
178
|
const generator = htmlGenerator`<div>!${generatorExample()}</div>`;
|
|
179
179
|
let accumulator = "";
|
|
180
180
|
|
|
@@ -189,6 +189,21 @@ test("htmlGenerator works with other generators", () => {
|
|
|
189
189
|
assert.strictEqual(generator.next().done, true);
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
test("htmlGenerator works with other generators (escaped)", () => {
|
|
193
|
+
const generator = htmlGenerator`<div>${generatorExample()}</div>`;
|
|
194
|
+
let accumulator = "";
|
|
195
|
+
|
|
196
|
+
for (const value of generator) {
|
|
197
|
+
accumulator += value;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
assert.strictEqual(
|
|
201
|
+
accumulator,
|
|
202
|
+
"<div><p>This is a safe description.<script>alert('This is an unsafe description.')</script>12345255</p></div>",
|
|
203
|
+
);
|
|
204
|
+
assert.strictEqual(generator.next().done, true);
|
|
205
|
+
});
|
|
206
|
+
|
|
192
207
|
test("htmlGenerator works with other generators within an array (raw)", () => {
|
|
193
208
|
const generator = htmlGenerator`<div>!${[generatorExample()]}</div>`;
|
|
194
209
|
let accumulator = "";
|
|
@@ -247,6 +262,34 @@ test("htmlAsyncGenerator renders unsafe content", async () => {
|
|
|
247
262
|
);
|
|
248
263
|
});
|
|
249
264
|
|
|
265
|
+
test("htmlAsyncGenerator works with other generators (raw)", async () => {
|
|
266
|
+
const generator = htmlAsyncGenerator`<div>!${generatorExample()}</div>`;
|
|
267
|
+
let accumulator = "";
|
|
268
|
+
|
|
269
|
+
for await (const value of generator) {
|
|
270
|
+
accumulator += value;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
assert.strictEqual(
|
|
274
|
+
accumulator,
|
|
275
|
+
"<div><p>This is a safe description.<script>alert('This is an unsafe description.')</script>12345255</p></div>",
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("htmlAsyncGenerator works with other generators (escaped)", async () => {
|
|
280
|
+
const generator = htmlAsyncGenerator`<div>${generatorExample()}</div>`;
|
|
281
|
+
let accumulator = "";
|
|
282
|
+
|
|
283
|
+
for await (const value of generator) {
|
|
284
|
+
accumulator += value;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
assert.strictEqual(
|
|
288
|
+
accumulator,
|
|
289
|
+
"<div><p>This is a safe description.<script>alert('This is an unsafe description.')</script>12345255</p></div>",
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
250
293
|
test("htmlAsyncGenerator works with nested htmlAsyncGenerator calls in an array", async () => {
|
|
251
294
|
const generator = htmlAsyncGenerator`!${[1, 2, 3].map((i) => {
|
|
252
295
|
return htmlAsyncGenerator`${i}: <p>${readFile("test/test.md", "utf8")}</p>`;
|