ghtml 1.2.3 → 1.2.5

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 CHANGED
@@ -12,11 +12,17 @@ npm i ghtml
12
12
 
13
13
  ### `html`
14
14
 
15
- The `html` function is used to tag template literals and escape their expressions. To bypass escaping an expression, prefix it with `!`.
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 is the generator version of the `html` function. It allows for the generation of HTML fragments in a streaming manner, which can be particularly useful for large templates or when generating HTML on-the-fly.
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
- const htmlContent = html`<html>
81
- <p>${"...your HTML content..."}</p>
82
- </html>`;
83
- const readableStream = Readable.from(htmlContent);
84
-
85
- http.createServer((req, res) => {
86
- res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
87
- readableStream.pipe(res);
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/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.3",
6
+ "version": "1.2.5",
7
7
  "type": "module",
8
8
  "main": "./src/index.js",
9
9
  "exports": {
package/src/html.js CHANGED
@@ -29,7 +29,7 @@ const html = ({ raw: literals }, ...expressions) => {
29
29
  let expression =
30
30
  typeof expressions[index] === "string"
31
31
  ? expressions[index]
32
- : expressions[index] == null
32
+ : expressions[index] === undefined || expressions[index] === null
33
33
  ? ""
34
34
  : Array.isArray(expressions[index])
35
35
  ? expressions[index].join("")
@@ -61,14 +61,15 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
61
61
 
62
62
  if (typeof expressions[index] === "string") {
63
63
  expression = expressions[index];
64
- } else if (expressions[index] == null) {
64
+ } else if (
65
+ expressions[index] === undefined ||
66
+ expressions[index] === null
67
+ ) {
65
68
  expression = "";
66
- } else if (Array.isArray(expressions[index])) {
67
- expression = expressions[index].join("");
68
69
  } else {
69
70
  if (typeof expressions[index][Symbol.iterator] === "function") {
70
71
  const isRaw =
71
- literal.length > 0 && literal.charCodeAt(literal.length - 1) === 33;
72
+ literal.length !== 0 && literal.charCodeAt(literal.length - 1) === 33;
72
73
 
73
74
  if (isRaw) {
74
75
  literal = literal.slice(0, -1);
@@ -79,14 +80,19 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
79
80
  }
80
81
 
81
82
  for (const value of expressions[index]) {
82
- expression =
83
- typeof value === "string"
84
- ? value
85
- : value == null
86
- ? ""
87
- : Array.isArray(value)
88
- ? value.join("")
89
- : `${value}`;
83
+ if (typeof value === "string") {
84
+ expression = value;
85
+ } else if (value === undefined || value === null) {
86
+ expression = "";
87
+ } else if (typeof value[Symbol.iterator] === "function") {
88
+ expression = "";
89
+
90
+ for (const innerValue of value) {
91
+ expression += innerValue ?? "";
92
+ }
93
+ } else {
94
+ expression = `${value}`;
95
+ }
90
96
 
91
97
  if (expression.length) {
92
98
  if (!isRaw) {
package/test/index.js CHANGED
@@ -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>${descriptionUnsafe}${descriptionUnsafe}${htmlGenerator`${array1}`}${null}${255}</p>`;
138
- assert.strictEqual(
139
- generator.next().value,
140
- "<p>&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;",
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
- generator.next().value,
144
- "&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;",
145
+ accumulator,
146
+ "<p>This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;12345255</p>",
145
147
  );
146
- assert.strictEqual(generator.next().value, "12345");
147
- assert.strictEqual(generator.next().value, "255");
148
- assert.strictEqual(generator.next().value, "</p>");
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
+ "&lt;p&gt;This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;1,2,3,4,5255&lt;/p&gt;",
197
+ );
198
+ assert.strictEqual(generator.next().value, "</div>");
199
+ assert.strictEqual(generator.next().done, true);
200
+ });