ghtml 1.2.4 → 1.5.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/.eslintrc.json CHANGED
@@ -1,4 +1,7 @@
1
1
  {
2
2
  "root": true,
3
- "extends": ["plugin:grules/all"]
3
+ "extends": ["plugin:grules/all"],
4
+ "rules": {
5
+ "no-await-in-loop": "off"
6
+ }
4
7
  }
@@ -1,16 +1,16 @@
1
- name: Benchmark Comparison
1
+ name: benchmark
2
+
3
+ on:
4
+ pull_request_target:
5
+ types: [labeled]
2
6
 
3
7
  permissions:
4
8
  contents: read
5
9
  pull-requests: write
6
10
 
7
- on:
8
- pull_request:
9
- branches:
10
- - main
11
-
12
11
  jobs:
13
- benchmark-comparison:
12
+ benchmark:
13
+ if: ${{ github.event.label.name == 'benchmark' }}
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
16
  # Checkout PR code
@@ -25,11 +25,11 @@ jobs:
25
25
 
26
26
  # Install dependencies
27
27
  - name: Install dependencies
28
- run: npm install
28
+ run: npm install --ignore-scripts
29
29
 
30
30
  # Run benchmark on PR code
31
31
  - name: Run benchmark on PR code
32
- run: node bench/index.js
32
+ run: npm run benchmark
33
33
  id: benchmark_pr
34
34
 
35
35
  # Save PR benchmark results
@@ -55,7 +55,7 @@ jobs:
55
55
 
56
56
  # Run benchmark on main branch src with PR's benchmark tooling
57
57
  - name: Run benchmark on main branch src
58
- run: node bench/index.js
58
+ run: npm run benchmark
59
59
  id: benchmark_main
60
60
 
61
61
  # Save main benchmark results
@@ -70,24 +70,31 @@ jobs:
70
70
  script: |
71
71
  const prResults = JSON.parse(Buffer.from(process.env.PR_RESULTS, 'base64').toString('utf8'));
72
72
  const mainResults = JSON.parse(Buffer.from(process.env.MAIN_RESULTS, 'base64').toString('utf8'));
73
-
74
73
  const commentBody = `
75
74
  Benchmark Results Comparison (${context.sha}):
76
-
77
75
  **PR Branch:**
78
76
  \`\`\`json
79
77
  ${JSON.stringify(prResults, null, 2)}
80
78
  \`\`\`
81
-
82
79
  **Main Branch:**
83
80
  \`\`\`json
84
81
  ${JSON.stringify(mainResults, null, 2)}
85
82
  \`\`\`
86
83
  `;
87
-
88
84
  github.rest.issues.createComment({
89
85
  issue_number: context.issue.number,
90
86
  owner: context.repo.owner,
91
87
  repo: context.repo.repo,
92
88
  body: commentBody
93
89
  });
90
+
91
+ remove-label:
92
+ needs: benchmark
93
+ runs-on: ubuntu-latest
94
+ steps:
95
+ - name: Remove benchmark label
96
+ uses: octokit/request-action@v2.x
97
+ with:
98
+ route: DELETE /repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/benchmark
99
+ env:
100
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -22,5 +22,5 @@ jobs:
22
22
  - uses: actions/setup-node@v4
23
23
  with:
24
24
  node-version: ${{ matrix.node-version }}
25
- - run: npm install
25
+ - run: npm install --ignore-scripts
26
26
  - run: npm test
package/README.md CHANGED
@@ -18,11 +18,13 @@ The `html` function is designed to tag template literals and automatically escap
18
18
 
19
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
20
 
21
- **Note:**
21
+ ### `htmlAsyncGenerator`
22
+
23
+ This version of HTML generator should be preferred for asynchronous use cases. The output will be generated as the promise expressions resolve.
22
24
 
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>`)``.
25
+ **Note:**
24
26
 
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.
27
+ 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>`)}``.
26
28
 
27
29
  ### `includeFile`
28
30
 
@@ -82,12 +84,33 @@ const htmlString = html`
82
84
  ```js
83
85
  import { htmlGenerator as html } from "ghtml";
84
86
  import { Readable } from "node:stream";
87
+ import http from "node:http";
88
+
89
+ http
90
+ .createServer((req, res) => {
91
+ const htmlContent = html`<html>
92
+ <p>${"...HTML content..."}</p>
93
+ </html>`;
94
+ const readableStream = Readable.from(htmlContent);
95
+ res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
96
+ readableStream.pipe(res);
97
+ })
98
+ .listen(3000);
99
+ ```
100
+
101
+ ### `htmlAsyncGenerator`
102
+
103
+ ```js
104
+ import { htmlAsyncGenerator as html } from "ghtml";
105
+ import { readFile } from "node:fs/promises";
106
+ import { Readable } from "node:stream";
107
+ import http from "node:http";
85
108
 
86
109
  http
87
110
  .createServer((req, res) => {
88
- const htmlContent = htmlGenerator`<html>
89
- <p>${"...HTML content..."}</p>
90
- </html>`;
111
+ const htmlContent = html`<html>
112
+ <code>${readFile("./README.md")}</code>
113
+ </html>`;
91
114
  const readableStream = Readable.from(htmlContent);
92
115
  res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
93
116
  readableStream.pipe(res);
package/bench/index.js CHANGED
@@ -1,84 +1,67 @@
1
+ /* eslint-disable no-unused-vars */
1
2
  /* eslint-disable no-unused-expressions */
2
3
  import { html } from "../src/index.js";
3
4
  import { Bench } from "tinybench";
4
5
  import { writeFileSync } from "node:fs";
5
6
  import { Buffer } from "node:buffer";
6
7
 
8
+ let result = "";
7
9
  const bench = new Bench({ time: 500 });
8
10
 
9
- bench.add("Simple formatting", () => {
10
- html`<div>Hello, world!</div>`;
11
+ bench.add("simple HTML formatting", () => {
12
+ result = html`<div>Hello, world!</div>`;
13
+ });
14
+
15
+ bench.add("null and undefined expressions", () => {
16
+ result = html`<p>${null} and ${undefined}</p>`;
11
17
  });
12
18
 
13
19
  const username = "User";
14
- bench.add("Using string variable", () => {
15
- html`<p>${username}</p>`;
20
+ bench.add("string expressions", () => {
21
+ result = html`<p>${username} and ${username}</p>`;
16
22
  });
17
23
 
18
- const value = null;
19
- const undef = undefined;
20
- bench.add("Handling null and undefined", () => {
21
- html`<p>${value} and ${undef}</p>`;
24
+ const items1 = ["Item 1", undefined, "Item 2", null, 2000, 1500.5];
25
+ bench.add("array expressions", () => {
26
+ result = html`<ul>
27
+ ${items1.map((item) => {
28
+ return html`<li>${item}</li>`;
29
+ })}
30
+ </ul>`;
22
31
  });
23
32
 
24
33
  const user = { id: 1, name: "John Doe" };
25
- bench.add("Multiple types of expressions", () => {
26
- html`
34
+ const items2 = ["Item 1", "Item 2", "Item 3"];
35
+ bench.add("multiple types of expressions", () => {
36
+ result = html`
27
37
  ${undefined}
28
38
  <div>User: <span>${user.name}</span></div>
29
39
  <div>Id: <span>${user.id}</span></div>
30
- ${null}
40
+ ${null}${123}${456n}
41
+ <ul>
42
+ !${items2.map((item) => {
43
+ return html`<li>${item}</li>`;
44
+ })}
45
+ </ul>
31
46
  `;
32
47
  });
33
48
 
34
- const items = ["Item 1", "Item 2", "Item 3"];
35
- bench.add("Arrays and iteration", () => {
36
- html`<ul>
37
- ${items.map((item) => {
38
- return html`<li>${item}</li>`;
39
- })}
40
- </ul>`;
41
- });
42
-
43
- const items2 = ["Item 1", undefined, "Item 2", null, 2000];
44
- bench.add("Arrays and iteration with multiple types", () => {
45
- html`<ul>
46
- ${items2.map((item) => {
47
- return html`<li>${item}</li>`;
48
- })}
49
- </ul>`;
50
- });
51
-
52
- const loggedIn = true;
53
- bench.add("Complex/nested expressions", () => {
54
- html`<nav>
55
- ${loggedIn
56
- ? html`<a href="/logout">Logout</a>`
57
- : html`<a href="/login">Login</a>`}
58
- </nav>`;
59
- });
60
-
61
49
  const largeString = Array.from({ length: 1000 }).join("Lorem ipsum ");
62
- bench.add("Large strings", () => {
63
- html`<p>${largeString}</p>`;
64
- });
65
-
66
- bench.add("High iteration count", () => {
67
- for (let i = 0; i !== 1000; i++) {
68
- html`<span>${i}</span>`;
69
- }
50
+ bench.add("large strings", () => {
51
+ result = html`<p>${largeString}${largeString}</p>`;
70
52
  });
71
53
 
72
54
  const scriptContent =
73
55
  "<script>console.log('This should not execute');</script>";
74
- bench.add("Escape HTML", () => {
75
- html`<div>${scriptContent} ${scriptContent}</div>`;
56
+ bench.add("high iteration count", () => {
57
+ for (let i = 0; i !== 100; i++) {
58
+ result = html`<span>${i}: ${scriptContent}</span>`;
59
+ }
76
60
  });
77
61
 
78
- // Render raw HTML
79
62
  const rawHTML = "<em>Italic</em> and <strong>bold</strong>";
80
63
  const markup = "<mark>Highlighted</mark>";
81
- bench.add("Unescaped expressions", () => {
64
+ bench.add("unescaped expressions", () => {
82
65
  html`
83
66
  <div>!${rawHTML}</div>
84
67
  <div>!${rawHTML}</div>
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.4",
6
+ "version": "1.5.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.js",
9
9
  "exports": {
@@ -14,7 +14,8 @@
14
14
  "node": ">=18"
15
15
  },
16
16
  "scripts": {
17
- "test": "npm run lint && c8 --100 node --test test/**",
17
+ "benchmark": "node bench/index.js",
18
+ "test": "npm run lint && c8 --100 node --test test/*.js",
18
19
  "lint": "eslint . && prettier --check .",
19
20
  "lint:fix": "eslint --fix . && prettier --write ."
20
21
  },
package/src/html.js CHANGED
@@ -27,10 +27,10 @@ const html = ({ raw: literals }, ...expressions) => {
27
27
  for (; index !== expressions.length; ++index) {
28
28
  let literal = literals[index];
29
29
  let expression =
30
- typeof expressions[index] === "string"
31
- ? expressions[index]
32
- : expressions[index] == null
33
- ? ""
30
+ expressions[index] === undefined || expressions[index] === null
31
+ ? ""
32
+ : typeof expressions[index] === "string"
33
+ ? expressions[index]
34
34
  : Array.isArray(expressions[index])
35
35
  ? expressions[index].join("")
36
36
  : `${expressions[index]}`;
@@ -59,14 +59,14 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
59
59
  let literal = literals[index];
60
60
  let expression;
61
61
 
62
- if (typeof expressions[index] === "string") {
63
- expression = expressions[index];
64
- } else if (expressions[index] == null) {
62
+ if (expressions[index] === undefined || expressions[index] === null) {
65
63
  expression = "";
64
+ } else if (typeof expressions[index] === "string") {
65
+ expression = expressions[index];
66
66
  } else {
67
67
  if (typeof expressions[index][Symbol.iterator] === "function") {
68
68
  const isRaw =
69
- literal.length > 0 && literal.charCodeAt(literal.length - 1) === 33;
69
+ literal.length !== 0 && literal.charCodeAt(literal.length - 1) === 33;
70
70
 
71
71
  if (isRaw) {
72
72
  literal = literal.slice(0, -1);
@@ -77,20 +77,138 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
77
77
  }
78
78
 
79
79
  for (const value of expressions[index]) {
80
- if (typeof value === "string") {
80
+ if (value === undefined || value === null) {
81
+ continue;
82
+ } else if (typeof value === "string") {
81
83
  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;
84
+ } else {
85
+ if (typeof value[Symbol.iterator] === "function") {
86
+ for (const innerValue of value) {
87
+ if (innerValue === undefined || innerValue === null) {
88
+ continue;
89
+ } else if (typeof innerValue === "string") {
90
+ expression = innerValue;
91
+ } else {
92
+ expression = `${innerValue}`;
93
+ }
94
+
95
+ if (expression.length) {
96
+ if (!isRaw) {
97
+ expression = expression.replace(
98
+ escapeRegExp,
99
+ escapeFunction,
100
+ );
101
+ }
102
+
103
+ yield expression;
104
+ }
91
105
  }
106
+
107
+ continue;
108
+ }
109
+
110
+ expression = `${value}`;
111
+ }
112
+
113
+ if (expression.length) {
114
+ if (!isRaw) {
115
+ expression = expression.replace(escapeRegExp, escapeFunction);
92
116
  }
117
+
118
+ yield expression;
119
+ }
120
+ }
121
+
122
+ continue;
123
+ }
124
+
125
+ expression = `${expressions[index]}`;
126
+ }
127
+
128
+ if (literal.length && literal.charCodeAt(literal.length - 1) === 33) {
129
+ literal = literal.slice(0, -1);
130
+ } else if (expression.length) {
131
+ expression = expression.replace(escapeRegExp, escapeFunction);
132
+ }
133
+
134
+ if (literal.length || expression.length) {
135
+ yield literal + expression;
136
+ }
137
+ }
138
+
139
+ if (literals[index].length) {
140
+ yield literals[index];
141
+ }
142
+ };
143
+
144
+ /**
145
+ * @param {{ raw: string[] }} literals Tagged template literals.
146
+ * @param {...any} expressions Expressions to interpolate.
147
+ * @yields {string} The HTML strings.
148
+ */
149
+ const htmlAsyncGenerator = async function* ({ raw: literals }, ...expressions) {
150
+ let index = 0;
151
+
152
+ for (; index !== expressions.length; ++index) {
153
+ let literal = literals[index];
154
+ let expression;
155
+
156
+ expressions[index] = await expressions[index];
157
+
158
+ if (expressions[index] === undefined || expressions[index] === null) {
159
+ expression = "";
160
+ } else if (typeof expressions[index] === "string") {
161
+ expression = expressions[index];
162
+ } else {
163
+ if (
164
+ typeof expressions[index][Symbol.iterator] === "function" ||
165
+ typeof expressions[index][Symbol.asyncIterator] === "function"
166
+ ) {
167
+ const isRaw =
168
+ literal.length !== 0 && literal.charCodeAt(literal.length - 1) === 33;
169
+
170
+ if (isRaw) {
171
+ literal = literal.slice(0, -1);
172
+ }
173
+
174
+ if (literal.length) {
175
+ yield literal;
176
+ }
177
+
178
+ for await (const value of expressions[index]) {
179
+ if (value === undefined || value === null) {
180
+ continue;
181
+ } else if (typeof value === "string") {
182
+ expression = value;
93
183
  } else {
184
+ if (
185
+ typeof value[Symbol.iterator] === "function" ||
186
+ typeof value[Symbol.asyncIterator] === "function"
187
+ ) {
188
+ for await (const innerValue of value) {
189
+ if (innerValue === undefined || innerValue === null) {
190
+ continue;
191
+ } else if (typeof innerValue === "string") {
192
+ expression = innerValue;
193
+ } else {
194
+ expression = `${innerValue}`;
195
+ }
196
+
197
+ if (expression.length) {
198
+ if (!isRaw) {
199
+ expression = expression.replace(
200
+ escapeRegExp,
201
+ escapeFunction,
202
+ );
203
+ }
204
+
205
+ yield expression;
206
+ }
207
+ }
208
+
209
+ continue;
210
+ }
211
+
94
212
  expression = `${value}`;
95
213
  }
96
214
 
@@ -125,4 +243,4 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
125
243
  }
126
244
  };
127
245
 
128
- export { html, htmlGenerator };
246
+ export { html, htmlGenerator, htmlAsyncGenerator };
package/src/index.js CHANGED
@@ -1 +1 @@
1
- export { html, htmlGenerator } from "./html.js";
1
+ export { html, htmlGenerator, htmlAsyncGenerator } from "./html.js";
package/test/index.js CHANGED
@@ -1,4 +1,6 @@
1
- import { html, htmlGenerator } from "../src/index.js";
1
+ import { html, htmlGenerator, htmlAsyncGenerator } from "../src/index.js";
2
+ import { readFile } from "node:fs/promises";
3
+ import { readFileSync } from "node:fs";
2
4
  import test from "node:test";
3
5
  import assert from "node:assert";
4
6
 
@@ -20,6 +22,17 @@ const generatorExample = function* () {
20
22
  yield "</p>";
21
23
  };
22
24
 
25
+ const generatorPromiseExample = function* () {
26
+ yield [
27
+ new Promise((resolve) => {
28
+ resolve("<p>");
29
+ }),
30
+ null,
31
+ 12n,
32
+ ];
33
+ yield;
34
+ };
35
+
23
36
  test("renders empty input", () => {
24
37
  assert.strictEqual(html({ raw: [""] }), "");
25
38
  });
@@ -163,38 +176,186 @@ test("htmlGenerator works with nested htmlGenerator calls in an array", () => {
163
176
 
164
177
  test("htmlGenerator works with other generators", () => {
165
178
  const generator = htmlGenerator`<div>!${generatorExample()}</div>`;
166
- assert.strictEqual(generator.next().value, "<div>");
167
- assert.strictEqual(generator.next().value, "<p>");
168
- assert.strictEqual(generator.next().value, "This is a safe description.");
179
+ let accumulator = "";
180
+
181
+ for (const value of generator) {
182
+ accumulator += value;
183
+ }
184
+
169
185
  assert.strictEqual(
170
- generator.next().value,
171
- "<script>alert('This is an unsafe description.')</script>",
186
+ accumulator,
187
+ "<div><p>This is a safe description.<script>alert('This is an unsafe description.')</script>12345255</p></div>",
172
188
  );
173
- assert.strictEqual(generator.next().value, "12345");
174
- assert.strictEqual(generator.next().value, "255");
175
- assert.strictEqual(generator.next().value, "</p>");
176
- assert.strictEqual(generator.next().value, "</div>");
177
189
  assert.strictEqual(generator.next().done, true);
178
190
  });
179
191
 
180
192
  test("htmlGenerator works with other generators within an array (raw)", () => {
181
193
  const generator = htmlGenerator`<div>!${[generatorExample()]}</div>`;
182
- assert.strictEqual(generator.next().value, "<div>");
194
+ let accumulator = "";
195
+
196
+ for (const value of generator) {
197
+ accumulator += value;
198
+ }
199
+
183
200
  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>",
201
+ accumulator,
202
+ "<div><p>This is a safe description.<script>alert('This is an unsafe description.')</script>1,2,3,4,5255</p></div>",
186
203
  );
187
- assert.strictEqual(generator.next().value, "</div>");
188
204
  assert.strictEqual(generator.next().done, true);
189
205
  });
190
206
 
191
207
  test("htmlGenerator works with other generators within an array (escaped)", () => {
192
208
  const generator = htmlGenerator`<div>${[generatorExample()]}</div>`;
193
- assert.strictEqual(generator.next().value, "<div>");
209
+ let accumulator = "";
210
+
211
+ for (const value of generator) {
212
+ accumulator += value;
213
+ }
214
+
194
215
  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;",
216
+ accumulator,
217
+ "<div>&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;</div>",
197
218
  );
198
- assert.strictEqual(generator.next().value, "</div>");
199
219
  assert.strictEqual(generator.next().done, true);
200
220
  });
221
+
222
+ test("htmlAsyncGenerator renders safe content", async () => {
223
+ const generator = htmlAsyncGenerator`<p>${descriptionSafe}!${descriptionUnsafe}G!${htmlAsyncGenerator`${array1}`}!${null}${255}</p>`;
224
+ let accumulator = "";
225
+
226
+ for await (const value of generator) {
227
+ accumulator += value;
228
+ }
229
+
230
+ assert.strictEqual(
231
+ accumulator,
232
+ "<p>This is a safe description.<script>alert('This is an unsafe description.')</script>G12345255</p>",
233
+ );
234
+ });
235
+
236
+ test("htmlAsyncGenerator renders unsafe content", async () => {
237
+ const generator = htmlAsyncGenerator`<p>${descriptionSafe}${descriptionUnsafe}${htmlAsyncGenerator`${array1}`}${null}${255}</p>`;
238
+ let accumulator = "";
239
+
240
+ for await (const value of generator) {
241
+ accumulator += value;
242
+ }
243
+
244
+ assert.strictEqual(
245
+ accumulator,
246
+ "<p>This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;12345255</p>",
247
+ );
248
+ });
249
+
250
+ test("htmlAsyncGenerator works with nested htmlAsyncGenerator calls in an array", async () => {
251
+ const generator = htmlAsyncGenerator`!${[1, 2, 3].map((i) => {
252
+ return htmlAsyncGenerator`${i}: <p>${readFile("test/test.md", "utf8")}</p>`;
253
+ })}`;
254
+ let accumulator = "";
255
+
256
+ for await (const value of generator) {
257
+ accumulator += value;
258
+ }
259
+
260
+ assert.strictEqual(
261
+ accumulator.replaceAll("\n", "").trim(),
262
+ "1: <p># test.md&gt;</p>2: <p># test.md&gt;</p>3: <p># test.md&gt;</p>",
263
+ );
264
+ });
265
+
266
+ test("htmlAsyncGenerator renders chunks with promises (escaped)", async () => {
267
+ const generator = htmlAsyncGenerator`<ul>!${[1, 2].map((i) => {
268
+ return htmlAsyncGenerator`${i}: ${readFile("test/test.md", "utf8")}`;
269
+ })}</ul>`;
270
+ const fileContent = readFileSync("test/test.md", "utf8").replaceAll(
271
+ ">",
272
+ "&gt;",
273
+ );
274
+
275
+ let value = await generator.next();
276
+ assert.strictEqual(value.value, "<ul>");
277
+
278
+ value = await generator.next();
279
+ assert.strictEqual(value.value, `1`);
280
+
281
+ value = await generator.next();
282
+ assert.strictEqual(value.value, `: ${fileContent}`);
283
+
284
+ value = await generator.next();
285
+ assert.strictEqual(value.value, `2`);
286
+
287
+ value = await generator.next();
288
+ assert.strictEqual(value.value, `: ${fileContent}`);
289
+
290
+ value = await generator.next();
291
+ assert.strictEqual(value.value, "</ul>");
292
+
293
+ value = await generator.next();
294
+ assert.strictEqual(value.done, true);
295
+ });
296
+
297
+ test("htmlAsyncGenerator renders chunks with promises (raw)", async () => {
298
+ const generator = htmlAsyncGenerator`<ul>!${[1, 2].map((i) => {
299
+ return htmlAsyncGenerator`${i}: !${readFile("test/test.md", "utf8")}`;
300
+ })}</ul>`;
301
+ const fileContent = readFileSync("test/test.md", "utf8");
302
+
303
+ let value = await generator.next();
304
+ assert.strictEqual(value.value, "<ul>");
305
+
306
+ value = await generator.next();
307
+ assert.strictEqual(value.value, `1`);
308
+
309
+ value = await generator.next();
310
+ assert.strictEqual(value.value, `: ${fileContent}`);
311
+
312
+ value = await generator.next();
313
+ assert.strictEqual(value.value, `2`);
314
+
315
+ value = await generator.next();
316
+ assert.strictEqual(value.value, `: ${fileContent}`);
317
+
318
+ value = await generator.next();
319
+ assert.strictEqual(value.value, "</ul>");
320
+
321
+ value = await generator.next();
322
+ assert.strictEqual(value.done, true);
323
+ });
324
+
325
+ test("htmlAsyncGenerator redners in chuncks", async () => {
326
+ const generator = htmlAsyncGenerator`<ul>${generatorPromiseExample()}</ul>`;
327
+
328
+ let value = await generator.next();
329
+ assert.strictEqual(value.value, "<ul>");
330
+
331
+ value = await generator.next();
332
+ assert.strictEqual(value.value, "&lt;p&gt;");
333
+
334
+ value = await generator.next();
335
+ assert.strictEqual(value.value, "12");
336
+
337
+ value = await generator.next();
338
+ assert.strictEqual(value.value, "</ul>");
339
+
340
+ value = await generator.next();
341
+ assert.strictEqual(value.done, true);
342
+ });
343
+
344
+ test("htmlAsyncGenerator redners in chuncks (raw)", async () => {
345
+ const generator = htmlAsyncGenerator`<ul>!${generatorPromiseExample()}</ul>`;
346
+
347
+ let value = await generator.next();
348
+ assert.strictEqual(value.value, "<ul>");
349
+
350
+ value = await generator.next();
351
+ assert.strictEqual(value.value, "<p>");
352
+
353
+ value = await generator.next();
354
+ assert.strictEqual(value.value, "12");
355
+
356
+ value = await generator.next();
357
+ assert.strictEqual(value.value, "</ul>");
358
+
359
+ value = await generator.next();
360
+ assert.strictEqual(value.done, true);
361
+ });
package/test/test.md ADDED
@@ -0,0 +1 @@
1
+ # test.md>