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.
- package/.github/dependabot.yml +6 -0
- package/.github/workflows/ci.yml +10 -1
- package/README.md +44 -0
- package/package.json +2 -2
- package/src/html.js +5 -10
- package/src/includeFile.js +2 -3
- package/test/index.js +13 -21
package/.github/workflows/ci.yml
CHANGED
|
@@ -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:
|
|
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, <img src="https://example.com/hacker.png"></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.
|
|
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.
|
|
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
|
-
|
|
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[
|
|
46
|
+
accumulator += literals.raw[expressions.length];
|
|
52
47
|
|
|
53
48
|
return accumulator;
|
|
54
49
|
};
|
package/src/includeFile.js
CHANGED
|
@@ -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 = "
|
|
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
|
|
12
|
+
const emptyString = "";
|
|
13
13
|
|
|
14
|
-
test("renders
|
|
15
|
-
assert.strictEqual(html({ raw: [] }
|
|
14
|
+
test("renders empty input", () => {
|
|
15
|
+
assert.strictEqual(html({ raw: [""] }), "");
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
test("renders
|
|
19
|
-
assert.strictEqual(html`${
|
|
18
|
+
test("renders empty input", () => {
|
|
19
|
+
assert.strictEqual(html`${emptyString}`, "");
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
test("renders
|
|
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><script>alert('This is an unsafe description.')</script></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><script>alert('This is an unsafe description.')</script></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 "
|
|
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
|
-
:
|
|
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
|
|
103
|
+
And also, false description of the object is true
|
|
112
104
|
</p>
|
|
113
105
|
`,
|
|
114
106
|
);
|