ghtml 1.0.0 → 1.1.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.
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: npm
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
@@ -1,6 +1,10 @@
1
1
  name: CI
2
2
 
3
3
  on:
4
+ push:
5
+ branches:
6
+ - main
7
+
4
8
  pull_request:
5
9
  branches:
6
10
  - main
@@ -9,9 +13,14 @@ jobs:
9
13
  test:
10
14
  runs-on: ubuntu-latest
11
15
 
16
+ strategy:
17
+ matrix:
18
+ node-version: [^18, lts/*]
19
+
12
20
  steps:
13
21
  - uses: actions/checkout@v4
14
22
  - uses: actions/setup-node@v4
15
23
  with:
16
- node-version: ^18
24
+ node-version: ${{ matrix.node-version }}
25
+ - run: npm install
17
26
  - run: npm run test
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ Replace your template engine with fast JavaScript by leveraging the power of [tagged templates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates).
2
+
3
+ Inspired by [html-template-tag](https://github.com/AntonioVdlC/html-template-tag).
4
+
5
+ ## Installation
6
+
7
+ ```shell
8
+ npm i ghtml
9
+ ```
10
+
11
+ ## API Reference
12
+
13
+ The main export of the package is the `html` function that can be used to tag template literals and escape their expressions. To bypass escaping an expression, prefix it with `!`.
14
+
15
+ Node.js users also have access to the `includeFile` function that reads and outputs the content of a file while caching it in memory for future use.
16
+
17
+ ## Usage
18
+
19
+ ```js
20
+ import { html } from "ghtml";
21
+
22
+ const username = '<img src="https://example.com/hacker.png">';
23
+ const greeting = html`<h1>Hello, ${username}!</h1>`;
24
+
25
+ console.log(greeting);
26
+ // Output: <h1>Hello, &lt;img src=&quot;https://example.com/hacker.png&quot;&gt;</h1>
27
+
28
+ const img = '<img src="https://example.com/safe.png">';
29
+ const container = html`<div>!${img}</div>`;
30
+
31
+ console.log(container);
32
+ // Output: <div><img src="https://example.com/safe.png"></div>
33
+ ```
34
+
35
+ The `includeFile` function returns the content of a file. Again, remember that it also caches the result, so any subsequent modifications to the same file won't be reflected until the app is restarted:
36
+
37
+ ```js
38
+ import { includeFile } from "ghtml/includeFile.js";
39
+
40
+ const logo = includeFile("static/logo.svg");
41
+
42
+ console.log(logo);
43
+ // Output: content of "static/logo.svg"
44
+ ```
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.0.0",
6
+ "version": "1.1.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.js",
9
9
  "exports": {
@@ -19,7 +19,7 @@
19
19
  "lint:fix": "eslint --fix . && prettier --write ."
20
20
  },
21
21
  "devDependencies": {
22
- "grules": "^0.5.0"
22
+ "grules": "^0.12.1"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",
package/src/html.js CHANGED
@@ -16,19 +16,14 @@ const escapeFunction = (key) => {
16
16
  };
17
17
 
18
18
  /**
19
- * @param {{ raw: string[] }} literals
20
- * @param {...any} expressions
21
- * @returns {string}
19
+ * @param {{ raw: string[] }} literals Tagged template literals.
20
+ * @param {...any} expressions Expressions to interpolate.
21
+ * @returns {string} The HTML string.
22
22
  */
23
23
  const html = (literals, ...expressions) => {
24
- const lastLiteralIndex = literals.raw.length - 1;
25
24
  let accumulator = "";
26
25
 
27
- if (lastLiteralIndex === -1) {
28
- return accumulator;
29
- }
30
-
31
- for (let index = 0; index < lastLiteralIndex; ++index) {
26
+ for (let index = 0; index < expressions.length; ++index) {
32
27
  let literal = literals.raw[index];
33
28
  let expression =
34
29
  typeof expressions[index] === "string"
@@ -48,7 +43,7 @@ const html = (literals, ...expressions) => {
48
43
  accumulator += literal + expression;
49
44
  }
50
45
 
51
- accumulator += literals.raw[lastLiteralIndex];
46
+ accumulator += literals.raw[expressions.length];
52
47
 
53
48
  return accumulator;
54
49
  };
@@ -1,12 +1,11 @@
1
1
  import { readFileSync } from "node:fs";
2
2
 
3
3
  const readFileSyncOptions = { encoding: "utf8" };
4
-
5
4
  const fileCache = new Map();
6
5
 
7
6
  /**
8
- * @param {string} path
9
- * @returns {string}
7
+ * @param {string} path The path to the file to render.
8
+ * @returns {string} The cached content of the file.
10
9
  */
11
10
  const includeFile = (path) => {
12
11
  let file = fileCache.get(path);
package/test/index.js CHANGED
@@ -2,24 +2,24 @@ import test from "node:test";
2
2
  import assert from "node:assert";
3
3
  import { html } from "../src/index.js";
4
4
 
5
- const username = "G";
5
+ const username = "Paul";
6
6
  const descriptionSafe = "This is a safe description.";
7
7
  const descriptionUnsafe =
8
8
  "<script>alert('This is an unsafe description.')</script>";
9
9
  const array1 = [1, 2, 3, 4, 5];
10
10
  const conditionTrue = true;
11
11
  const conditionFalse = false;
12
- const empty = "";
12
+ const emptyString = "";
13
13
 
14
- test("renders correctly", () => {
15
- assert.strictEqual(html({ raw: [] }, []), "");
14
+ test("renders empty input", () => {
15
+ assert.strictEqual(html({ raw: [""] }), "");
16
16
  });
17
17
 
18
- test("renders correctly", () => {
19
- assert.strictEqual(html`${empty}`, "");
18
+ test("renders empty input", () => {
19
+ assert.strictEqual(html`${emptyString}`, "");
20
20
  });
21
21
 
22
- test("renders correctly", () => {
22
+ test("renders normal input", () => {
23
23
  assert.strictEqual(html`Hey, ${username}!`, `Hey, ${username}!`);
24
24
  });
25
25
 
@@ -37,13 +37,6 @@ test("escapes unsafe output", () => {
37
37
  );
38
38
  });
39
39
 
40
- test("escapes unsafe output", () => {
41
- assert.strictEqual(
42
- html`<p>${descriptionUnsafe}</p>`,
43
- `<p>&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;</p>`,
44
- );
45
- });
46
-
47
40
  test("renders arrays", () => {
48
41
  assert.strictEqual(
49
42
  html`<p>${[descriptionSafe, descriptionUnsafe]}</p>`,
@@ -59,8 +52,8 @@ test("bypass escaping", () => {
59
52
  });
60
53
 
61
54
  test("renders wrapped html calls", () => {
55
+ // prettier-ignore
62
56
  assert.strictEqual(
63
- // prettier-ignore
64
57
  html`<p>!${conditionTrue ? html`<strong>${descriptionUnsafe}</strong>` : ""}</p>`,
65
58
  "<p><strong>&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;</strong></p>",
66
59
  );
@@ -75,7 +68,6 @@ test("renders multiple html calls", () => {
75
68
  !${conditionFalse ? html`<em> ${array1} </em>` : ""}
76
69
  </p>
77
70
  `,
78
- // it should be formatted
79
71
  `
80
72
  <p>
81
73
  <strong> This is a safe description. </strong>
@@ -89,26 +81,26 @@ test("renders multiple html calls", () => {
89
81
  test("renders multiple html calls with different expression types", () => {
90
82
  const obj = {};
91
83
  obj.toString = () => {
92
- return "Description of the object.";
84
+ return "description of the object";
93
85
  };
94
86
 
87
+ // prettier-ignore
95
88
  assert.strictEqual(
96
89
  html`
97
90
  <p>
98
91
  !${conditionTrue ? html`<strong> ${descriptionSafe} </strong>` : ""}
99
92
  !${conditionFalse
100
93
  ? ""
101
- : // prettier-ignore
94
+ :
102
95
  html`<em> ${array1.map((i) => {return i + 1;})} </em>`}<br />
103
- And also, ${false} ${null}${undefined}${obj}
96
+ And also, ${false} ${null}${undefined}${obj} is ${true}
104
97
  </p>
105
98
  `,
106
- // it should be formatted
107
99
  `
108
100
  <p>
109
101
  <strong> This is a safe description. </strong>
110
102
  <em> 23456 </em><br />
111
- And also, false Description of the object.
103
+ And also, false description of the object is true
112
104
  </p>
113
105
  `,
114
106
  );