ghtml 2.0.0 → 2.0.2

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
@@ -2,6 +2,7 @@
2
2
  "root": true,
3
3
  "extends": ["plugin:grules/all"],
4
4
  "rules": {
5
- "no-await-in-loop": "off"
5
+ "no-await-in-loop": "off",
6
+ "require-unicode-regexp": "off"
6
7
  }
7
8
  }
package/README.md CHANGED
@@ -20,11 +20,11 @@ The `htmlGenerator` function acts as the generator version of the `html` functio
20
20
 
21
21
  **Note:**
22
22
 
23
- Keep in mind that, in Node.js, all else being equal, streaming a response using synchronous generators will **always** be slower than processing everything directly and sending it at once — [this also applies to TTFB](https://github.com/mcollina/fastify-html/issues/11#issuecomment-2069385895). However, if a template includes promises that do asynchronous operations (I/O, etc.), then `htmlAsyncGenerator` can be used to stream the response as those promises get resolved, which will indeed improve TTFB.
23
+ Keep in mind that, in Node.js, all else being equal, streaming a response using synchronous generators is **always** slower than processing everything directly and sending it at once — [this also applies to TTFB](https://github.com/mcollina/fastify-html/issues/11#issuecomment-2069385895). However, if a template includes promises that do asynchronous operations (I/O, etc.), then `htmlAsyncGenerator` can be used to stream the response as those promises get resolved, which does indeed improve TTFB.
24
24
 
25
25
  ### `htmlAsyncGenerator`
26
26
 
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.
27
+ This version of HTML generator should be preferred for asynchronous and streaming use cases. The output is generated as the promise expressions resolve or stream expressions send data.
28
28
 
29
29
  **Minor Note:**
30
30
 
@@ -42,7 +42,7 @@ Available in Node.js, the `includeFile` function is a wrapper around `readFileSy
42
42
  import { html } from "ghtml";
43
43
 
44
44
  const username = '<img src="https://example.com/pwned.png">';
45
- const greeting = html`<h1>Hello, ${username}!</h1>`;
45
+ const greeting = html`<h1>Hello, ${username}</h1>`;
46
46
 
47
47
  console.log(greeting);
48
48
  // Output: <h1>Hello, &#60;img src=&#34;https://example.com/pwned.png&#34;&#62;</h1>
@@ -58,7 +58,7 @@ console.log(container);
58
58
  // Output: <div><img src="https://example.com/safe.png"></div>
59
59
  ```
60
60
 
61
- When nesting multiple `html` expressions, always use `!` as they will do their own escaping:
61
+ When nesting multiple `html` expressions, make sure to use `!` as the inner calls do their own escaping:
62
62
 
63
63
  ```js
64
64
  const someCondition = Math.random() >= 0.5;
@@ -171,4 +171,4 @@ console.log(logo);
171
171
 
172
172
  ## Security
173
173
 
174
- Like [similar](https://handlebarsjs.com/guide/#html-escaping) [tools](https://github.com/mde/ejs/blob/main/SECURITY.md#out-of-scope-vulnerabilities), `ghtml` will not prevent all kinds of XSS attacks. It is the responsibility of consumers to sanitize user inputs. Some inherently insecure uses include dynamically generating JavaScript, failing to quote HTML attribute values (especially when they contain expressions), and using unsanitized user-provided URLs.
174
+ Like [similar](https://handlebarsjs.com/guide/#html-escaping) [tools](https://github.com/mde/ejs/blob/main/SECURITY.md#out-of-scope-vulnerabilities), `ghtml` does not prevent all kinds of XSS attacks. It is the responsibility of consumers to sanitize user inputs. Some inherently insecure uses include dynamically generating JavaScript, failing to quote HTML attribute values (especially when they contain expressions), and using unsanitized user-provided URLs.
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": "2.0.0",
6
+ "version": "2.0.2",
7
7
  "type": "module",
8
8
  "main": "./src/index.js",
9
9
  "exports": {
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "devDependencies": {
23
23
  "@fastify/pre-commit": "^2.1.0",
24
- "c8": "^9.1.0",
24
+ "c8": "^10.0.0",
25
25
  "grules": "^0.17.2",
26
26
  "tinybench": "^2.8.0"
27
27
  },
package/src/html.js CHANGED
@@ -1,3 +1,9 @@
1
+ const arrayIsArray = Array.isArray;
2
+
3
+ const symbolIterator = Symbol.iterator;
4
+
5
+ const symbolAsyncIterator = Symbol.asyncIterator;
6
+
1
7
  const escapeDictionary = {
2
8
  '"': "&#34;",
3
9
  "&": "&#38;",
@@ -7,10 +13,7 @@ const escapeDictionary = {
7
13
  "`": "&#96;",
8
14
  };
9
15
 
10
- const escapeRegExp = new RegExp(
11
- `[${Object.keys(escapeDictionary).join("")}]`,
12
- "u",
13
- );
16
+ const escapeRegExp = new RegExp(`[${Object.keys(escapeDictionary).join("")}]`);
14
17
 
15
18
  const escapeFunction = (string) => {
16
19
  const stringLength = string.length;
@@ -30,8 +33,6 @@ const escapeFunction = (string) => {
30
33
  return escaped + string.slice(start, end);
31
34
  };
32
35
 
33
- const arrayIsArray = Array.isArray;
34
-
35
36
  /**
36
37
  * @param {{ raw: string[] }} literals Tagged template literals.
37
38
  * @param {...any} expressions Expressions to interpolate.
@@ -85,7 +86,7 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
85
86
  } else if (expression === undefined || expression === null) {
86
87
  string = "";
87
88
  } else {
88
- if (expression[Symbol.iterator]) {
89
+ if (expression[symbolIterator]) {
89
90
  const isRaw =
90
91
  literal !== "" && literal.charCodeAt(literal.length - 1) === 33;
91
92
 
@@ -105,13 +106,17 @@ const htmlGenerator = function* ({ raw: literals }, ...expressions) {
105
106
  continue;
106
107
  }
107
108
 
108
- if (expression[Symbol.iterator]) {
109
+ if (expression[symbolIterator]) {
109
110
  for (expression of expression) {
110
- if (expression === undefined || expression === null) {
111
- continue;
112
- }
111
+ if (typeof expression === "string") {
112
+ string = expression;
113
+ } else {
114
+ if (expression === undefined || expression === null) {
115
+ continue;
116
+ }
113
117
 
114
- string = `${expression}`;
118
+ string = `${expression}`;
119
+ }
115
120
 
116
121
  if (string) {
117
122
  if (!isRaw && escapeRegExp.test(string)) {
@@ -178,7 +183,7 @@ const htmlAsyncGenerator = async function* ({ raw: literals }, ...expressions) {
178
183
  } else if (expression === undefined || expression === null) {
179
184
  string = "";
180
185
  } else {
181
- if (expression[Symbol.iterator] || expression[Symbol.asyncIterator]) {
186
+ if (expression[symbolIterator] || expression[symbolAsyncIterator]) {
182
187
  const isRaw =
183
188
  literal !== "" && literal.charCodeAt(literal.length - 1) === 33;
184
189
 
@@ -198,16 +203,17 @@ const htmlAsyncGenerator = async function* ({ raw: literals }, ...expressions) {
198
203
  continue;
199
204
  }
200
205
 
201
- if (
202
- expression[Symbol.iterator] ||
203
- expression[Symbol.asyncIterator]
204
- ) {
206
+ if (expression[symbolIterator] || expression[symbolAsyncIterator]) {
205
207
  for await (expression of expression) {
206
- if (expression === undefined || expression === null) {
207
- continue;
208
- }
208
+ if (typeof expression === "string") {
209
+ string = expression;
210
+ } else {
211
+ if (expression === undefined || expression === null) {
212
+ continue;
213
+ }
209
214
 
210
- string = `${expression}`;
215
+ string = `${expression}`;
216
+ }
211
217
 
212
218
  if (string) {
213
219
  if (!isRaw && escapeRegExp.test(string)) {