ghtml 3.0.7 → 3.0.9
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/LICENSE +1 -1
- package/bench/index.js +3 -3
- package/bin/README.md +1 -1
- package/bin/example/assets/script.js +1 -1
- package/bin/example/package.json +2 -2
- package/bin/example/routes/index.js +1 -1
- package/bin/example/server.js +4 -7
- package/bin/src/index.js +9 -7
- package/bin/src/utils.js +8 -2
- package/eslint.config.js +14 -0
- package/package.json +4 -4
- package/src/includeFile.js +2 -2
- package/src/index.d.ts +3 -1
- package/src/index.js +246 -1
- package/test/index.js +39 -0
- package/.eslintrc.json +0 -8
- package/bin/example/.eslintrc.json +0 -6
- package/src/html.d.ts +0 -3
- package/src/html.js +0 -262
package/LICENSE
CHANGED
package/bench/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
|
-
import { html } from "
|
|
2
|
+
import { html } from "ghtml";
|
|
3
3
|
import { Bench } from "tinybench";
|
|
4
4
|
import { writeFileSync } from "node:fs";
|
|
5
5
|
import { Buffer } from "node:buffer";
|
|
6
6
|
|
|
7
|
-
let result = "";
|
|
8
7
|
const bench = new Bench({ time: 500 });
|
|
8
|
+
let result = "";
|
|
9
9
|
|
|
10
10
|
bench.add("simple HTML formatting", () => {
|
|
11
11
|
result = html`<div>Hello, world!</div>`;
|
|
@@ -112,7 +112,7 @@ await bench.warmup();
|
|
|
112
112
|
await bench.run();
|
|
113
113
|
|
|
114
114
|
const table = bench.table();
|
|
115
|
-
console.table(table);
|
|
115
|
+
globalThis.console.table(table);
|
|
116
116
|
|
|
117
117
|
writeFileSync(
|
|
118
118
|
"bench/results.json",
|
package/bin/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Append unique hashes to assets referenced in your views to aggressively cache th
|
|
|
5
5
|
Running the following command will scan asset files found in the `roots` path(s) and replace their references in the `refs` path(s) with hashed versions:
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
npx ghtml --roots="base/path/to/scan/assets/1/,base/path/to/scan/assets/2/" --refs="views/path/to/append/hashes/1/,views/path/to/append/hashes/2/"
|
|
8
|
+
npx ghtml --roots="base/path/to/scan/assets/1/,base/path/to/scan/assets/2/" --refs="views/path/to/append/hashes/1/,views/path/to/append/hashes/2/" --prefix="/optional/prefix/"
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Example (Fastify)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
console.
|
|
1
|
+
globalThis.console.log("Hello World!");
|
package/bin/example/package.json
CHANGED
package/bin/example/server.js
CHANGED
|
@@ -4,7 +4,7 @@ const fastify = Fastify();
|
|
|
4
4
|
|
|
5
5
|
// Plugins
|
|
6
6
|
await fastify.register(import("@fastify/static"), {
|
|
7
|
-
root: new URL("assets/", import.meta.url).pathname,
|
|
7
|
+
root: new globalThis.URL("assets/", import.meta.url).pathname,
|
|
8
8
|
prefix: "/p/assets/",
|
|
9
9
|
wildcard: false,
|
|
10
10
|
index: false,
|
|
@@ -15,10 +15,7 @@ await fastify.register(import("@fastify/static"), {
|
|
|
15
15
|
// Routes
|
|
16
16
|
fastify.register(import("./routes/index.js"));
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
throw err;
|
|
21
|
-
}
|
|
18
|
+
// Listen
|
|
19
|
+
const address = await fastify.listen({ port: 5050 });
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
});
|
|
21
|
+
globalThis.console.log(`Server listening at ${address}`);
|
package/bin/src/index.js
CHANGED
|
@@ -18,7 +18,7 @@ const parseArguments = (args) => {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
if (!roots || !refs) {
|
|
21
|
-
console.
|
|
21
|
+
globalThis.console.log(
|
|
22
22
|
'Usage: npx ghtml --roots="base/path/to/scan/assets/1/,base/path/to/scan/assets/2/" --refs="views/path/to/append/hashes/1/,views/path/to/append/hashes/2/" [--prefix="/optional/prefix/"]',
|
|
23
23
|
);
|
|
24
24
|
process.exit(1);
|
|
@@ -31,10 +31,10 @@ const main = async () => {
|
|
|
31
31
|
const { roots, refs, prefix } = parseArguments(process.argv.slice(2));
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
|
-
console.
|
|
35
|
-
console.
|
|
36
|
-
console.
|
|
37
|
-
console.
|
|
34
|
+
globalThis.console.log(`Generating hashes and updating file paths...`);
|
|
35
|
+
globalThis.console.log(`Scanning files in: ${roots}`);
|
|
36
|
+
globalThis.console.log(`Updating files in: ${refs}`);
|
|
37
|
+
globalThis.console.log(`Using prefix: ${prefix}`);
|
|
38
38
|
|
|
39
39
|
await generateHashesAndReplace({
|
|
40
40
|
roots,
|
|
@@ -42,9 +42,11 @@ const main = async () => {
|
|
|
42
42
|
prefix,
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
console.
|
|
45
|
+
globalThis.console.log(
|
|
46
|
+
"Hash generation and file updates completed successfully.",
|
|
47
|
+
);
|
|
46
48
|
} catch (error) {
|
|
47
|
-
console.
|
|
49
|
+
globalThis.console.log(`Error occurred: ${error.message}`);
|
|
48
50
|
process.exit(1);
|
|
49
51
|
}
|
|
50
52
|
};
|
package/bin/src/utils.js
CHANGED
|
@@ -6,11 +6,13 @@ import { win32, posix } from "node:path";
|
|
|
6
6
|
const generateFileHash = async (filePath) => {
|
|
7
7
|
try {
|
|
8
8
|
const fileBuffer = await readFile(filePath);
|
|
9
|
+
|
|
9
10
|
return createHash("md5").update(fileBuffer).digest("hex").slice(0, 16);
|
|
10
11
|
} catch (err) {
|
|
11
12
|
if (err.code !== "ENOENT") {
|
|
12
13
|
throw err;
|
|
13
14
|
}
|
|
15
|
+
|
|
14
16
|
return "";
|
|
15
17
|
}
|
|
16
18
|
};
|
|
@@ -24,6 +26,7 @@ const updateFilePathsWithHashes = async (
|
|
|
24
26
|
) => {
|
|
25
27
|
for (let ref of refs) {
|
|
26
28
|
ref = ref.replaceAll(win32.sep, posix.sep);
|
|
29
|
+
|
|
27
30
|
if (!ref.endsWith("/")) {
|
|
28
31
|
ref += "/";
|
|
29
32
|
}
|
|
@@ -44,12 +47,12 @@ const updateFilePathsWithHashes = async (
|
|
|
44
47
|
for (const [path, hash] of fileHashes) {
|
|
45
48
|
const fullPath = prefix + path;
|
|
46
49
|
const escapedPath = fullPath.replace(
|
|
47
|
-
/[$()*+.?[\\\]^{|}]/
|
|
50
|
+
/[$()*+.?[\\\]^{|}]/gu,
|
|
48
51
|
String.raw`\$&`,
|
|
49
52
|
);
|
|
50
53
|
const regex = new RegExp(
|
|
51
54
|
`(?<path>${escapedPath})(\\?(?<queryString>[^#"'\`]*))?`,
|
|
52
|
-
"
|
|
55
|
+
"gu",
|
|
53
56
|
);
|
|
54
57
|
|
|
55
58
|
content = content.replace(
|
|
@@ -82,11 +85,13 @@ export const generateHashesAndReplace = async ({
|
|
|
82
85
|
skipPatterns = ["**/node_modules/**"],
|
|
83
86
|
}) => {
|
|
84
87
|
const fileHashes = new Map();
|
|
88
|
+
|
|
85
89
|
roots = Array.isArray(roots) ? roots : [roots];
|
|
86
90
|
refs = Array.isArray(refs) ? refs : [refs];
|
|
87
91
|
|
|
88
92
|
for (let root of roots) {
|
|
89
93
|
root = root.replaceAll(win32.sep, posix.sep);
|
|
94
|
+
|
|
90
95
|
if (!root.endsWith("/")) {
|
|
91
96
|
root += "/";
|
|
92
97
|
}
|
|
@@ -112,6 +117,7 @@ export const generateHashesAndReplace = async ({
|
|
|
112
117
|
|
|
113
118
|
for (let i = 0; i < files.length; ++i) {
|
|
114
119
|
const fileRelativePath = posix.relative(root, files[i]);
|
|
120
|
+
|
|
115
121
|
fileHashes.set(fileRelativePath, hashes[i]);
|
|
116
122
|
}
|
|
117
123
|
}
|
package/eslint.config.js
ADDED
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": "3.0.
|
|
6
|
+
"version": "3.0.9",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"bin": "./bin/src/index.js",
|
|
9
9
|
"main": "./src/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"test": "npm run lint && c8 --100 node --test test/*.js",
|
|
20
20
|
"lint": "eslint . && prettier --check .",
|
|
21
21
|
"lint:fix": "eslint --fix . && prettier --write .",
|
|
22
|
-
"typescript": "tsc src/*.js --allowJs --declaration --emitDeclarationOnly"
|
|
22
|
+
"typescript": "tsc src/*.js --allowJs --declaration --emitDeclarationOnly --skipLibCheck"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"glob": "^10.4.5"
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@fastify/pre-commit": "^2.1.0",
|
|
29
29
|
"c8": "^10.1.2",
|
|
30
|
-
"grules": "^0.
|
|
30
|
+
"grules": "^0.25.3",
|
|
31
31
|
"tinybench": "^2.9.0",
|
|
32
|
-
"typescript": ">=5.
|
|
32
|
+
"typescript": ">=5.6.2"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
package/src/includeFile.js
CHANGED
|
@@ -3,8 +3,8 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
const cache = new Map();
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @param {string} path
|
|
7
|
-
* @returns {string}
|
|
6
|
+
* @param {string} path path
|
|
7
|
+
* @returns {string} string
|
|
8
8
|
*/
|
|
9
9
|
export const includeFile = (path) => {
|
|
10
10
|
let file = cache.get(path);
|
package/src/index.d.ts
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export
|
|
1
|
+
export function html(literals: TemplateStringsArray, ...expressions: any[]): string;
|
|
2
|
+
export function htmlGenerator(literals: TemplateStringsArray, ...expressions: any[]): Generator<string, void, void>;
|
|
3
|
+
export function htmlAsyncGenerator(literals: TemplateStringsArray, ...expressions: any[]): AsyncGenerator<string, void, void>;
|
package/src/index.js
CHANGED
|
@@ -1 +1,246 @@
|
|
|
1
|
-
|
|
1
|
+
const escapeRegExp = /["&'<=>]/g;
|
|
2
|
+
|
|
3
|
+
const escapeFunction = (string) => {
|
|
4
|
+
let escaped = "";
|
|
5
|
+
let start = 0;
|
|
6
|
+
|
|
7
|
+
while (escapeRegExp.test(string)) {
|
|
8
|
+
const i = escapeRegExp.lastIndex - 1;
|
|
9
|
+
|
|
10
|
+
switch (string.charCodeAt(i)) {
|
|
11
|
+
case 34:
|
|
12
|
+
escaped += string.slice(start, i) + """; // "
|
|
13
|
+
break;
|
|
14
|
+
case 38:
|
|
15
|
+
escaped += string.slice(start, i) + "&"; // &
|
|
16
|
+
break;
|
|
17
|
+
case 39:
|
|
18
|
+
escaped += string.slice(start, i) + "'"; // '
|
|
19
|
+
break;
|
|
20
|
+
case 60:
|
|
21
|
+
escaped += string.slice(start, i) + "<"; // <
|
|
22
|
+
break;
|
|
23
|
+
case 61:
|
|
24
|
+
escaped += string.slice(start, i) + "="; // =
|
|
25
|
+
break;
|
|
26
|
+
default: {
|
|
27
|
+
escaped += string.slice(start, i) + ">"; // >
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
start = escapeRegExp.lastIndex;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return escaped + string.slice(start);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {TemplateStringsArray} literals literals
|
|
39
|
+
* @param {...any} expressions expressions
|
|
40
|
+
* @returns {string} string
|
|
41
|
+
*/
|
|
42
|
+
export const html = (literals, ...expressions) => {
|
|
43
|
+
let accumulator = "";
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i !== expressions.length; ++i) {
|
|
46
|
+
let literal = literals.raw[i];
|
|
47
|
+
let string = Array.isArray(expressions[i])
|
|
48
|
+
? expressions[i].join("")
|
|
49
|
+
: String(expressions[i] ?? "");
|
|
50
|
+
|
|
51
|
+
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
52
|
+
literal = literal.slice(0, -1);
|
|
53
|
+
} else {
|
|
54
|
+
string &&= escapeFunction(string);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
accumulator += literal + string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return accumulator + literals.raw[expressions.length];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {TemplateStringsArray} literals literals
|
|
65
|
+
* @param {...any} expressions expressions
|
|
66
|
+
* @yields {string} string
|
|
67
|
+
* @returns {Generator<string, void, void>} Generator<string, void, void>
|
|
68
|
+
*/
|
|
69
|
+
export const htmlGenerator = function* (literals, ...expressions) {
|
|
70
|
+
for (let i = 0; i !== expressions.length; ++i) {
|
|
71
|
+
let expression = expressions[i];
|
|
72
|
+
let literal = literals.raw[i];
|
|
73
|
+
let string;
|
|
74
|
+
|
|
75
|
+
if (typeof expression === "string") {
|
|
76
|
+
string = expression;
|
|
77
|
+
} else if (expression === undefined || expression === null) {
|
|
78
|
+
string = "";
|
|
79
|
+
} else {
|
|
80
|
+
if (expression[Symbol.iterator]) {
|
|
81
|
+
const isRaw =
|
|
82
|
+
Boolean(literal) && literal.charCodeAt(literal.length - 1) === 33;
|
|
83
|
+
|
|
84
|
+
if (isRaw) {
|
|
85
|
+
literal = literal.slice(0, -1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (literal) {
|
|
89
|
+
yield literal;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (expression of expression) {
|
|
93
|
+
if (typeof expression === "string") {
|
|
94
|
+
string = expression;
|
|
95
|
+
} else {
|
|
96
|
+
if (expression === undefined || expression === null) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (expression[Symbol.iterator]) {
|
|
101
|
+
for (expression of expression) {
|
|
102
|
+
if (expression === undefined || expression === null) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
string = String(expression);
|
|
107
|
+
|
|
108
|
+
if (string) {
|
|
109
|
+
if (!isRaw) {
|
|
110
|
+
string = escapeFunction(string);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
yield string;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
string = String(expression);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (string) {
|
|
124
|
+
if (!isRaw) {
|
|
125
|
+
string = escapeFunction(string);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
yield string;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
string = String(expression);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
139
|
+
literal = literal.slice(0, -1);
|
|
140
|
+
} else {
|
|
141
|
+
string &&= escapeFunction(string);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (literal || string) {
|
|
145
|
+
yield literal + string;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (literals.raw[expressions.length]) {
|
|
150
|
+
yield literals.raw[expressions.length];
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {TemplateStringsArray} literals literals
|
|
156
|
+
* @param {...any} expressions expressions
|
|
157
|
+
* @yields {string} string
|
|
158
|
+
* @returns {AsyncGenerator<string, void, void>} AsyncGenerator<string, void, void>
|
|
159
|
+
*/
|
|
160
|
+
export const htmlAsyncGenerator = async function* (literals, ...expressions) {
|
|
161
|
+
for (let i = 0; i !== expressions.length; ++i) {
|
|
162
|
+
let expression = await expressions[i];
|
|
163
|
+
let literal = literals.raw[i];
|
|
164
|
+
let string;
|
|
165
|
+
|
|
166
|
+
if (typeof expression === "string") {
|
|
167
|
+
string = expression;
|
|
168
|
+
} else if (expression === undefined || expression === null) {
|
|
169
|
+
string = "";
|
|
170
|
+
} else {
|
|
171
|
+
if (expression[Symbol.iterator] || expression[Symbol.asyncIterator]) {
|
|
172
|
+
const isRaw =
|
|
173
|
+
Boolean(literal) && literal.charCodeAt(literal.length - 1) === 33;
|
|
174
|
+
|
|
175
|
+
if (isRaw) {
|
|
176
|
+
literal = literal.slice(0, -1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (literal) {
|
|
180
|
+
yield literal;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for await (expression of expression) {
|
|
184
|
+
if (typeof expression === "string") {
|
|
185
|
+
string = expression;
|
|
186
|
+
} else {
|
|
187
|
+
if (expression === undefined || expression === null) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (
|
|
192
|
+
expression[Symbol.iterator] ||
|
|
193
|
+
expression[Symbol.asyncIterator]
|
|
194
|
+
) {
|
|
195
|
+
for await (expression of expression) {
|
|
196
|
+
if (expression === undefined || expression === null) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
string = String(expression);
|
|
201
|
+
|
|
202
|
+
if (string) {
|
|
203
|
+
if (!isRaw) {
|
|
204
|
+
string = escapeFunction(string);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
yield string;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
string = String(expression);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (string) {
|
|
218
|
+
if (!isRaw) {
|
|
219
|
+
string = escapeFunction(string);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
yield string;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
string = String(expression);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
233
|
+
literal = literal.slice(0, -1);
|
|
234
|
+
} else {
|
|
235
|
+
string &&= escapeFunction(string);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (literal || string) {
|
|
239
|
+
yield literal + string;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (literals.raw[expressions.length]) {
|
|
244
|
+
yield literals.raw[expressions.length];
|
|
245
|
+
}
|
|
246
|
+
};
|
package/test/index.js
CHANGED
|
@@ -22,6 +22,11 @@ const generatorExample = function* () {
|
|
|
22
22
|
yield "</p>";
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
const generatorExample2 = function* () {
|
|
26
|
+
yield ["", "<p>"];
|
|
27
|
+
yield "";
|
|
28
|
+
};
|
|
29
|
+
|
|
25
30
|
const generatorPromiseExample = function* () {
|
|
26
31
|
yield [
|
|
27
32
|
new Promise((resolve) => {
|
|
@@ -33,6 +38,17 @@ const generatorPromiseExample = function* () {
|
|
|
33
38
|
yield;
|
|
34
39
|
};
|
|
35
40
|
|
|
41
|
+
const generatorPromiseExample2 = function* () {
|
|
42
|
+
yield [
|
|
43
|
+
new Promise((resolve) => {
|
|
44
|
+
resolve("<p>");
|
|
45
|
+
}),
|
|
46
|
+
null,
|
|
47
|
+
"",
|
|
48
|
+
];
|
|
49
|
+
yield "";
|
|
50
|
+
};
|
|
51
|
+
|
|
36
52
|
test("renders empty input", () => {
|
|
37
53
|
assert.strictEqual(html({ raw: [""] }), "");
|
|
38
54
|
});
|
|
@@ -204,6 +220,18 @@ test("htmlGenerator works with other generators (raw)", () => {
|
|
|
204
220
|
assert.strictEqual(generator.next().done, true);
|
|
205
221
|
});
|
|
206
222
|
|
|
223
|
+
test("htmlGenerator works with other generators (raw) /2", () => {
|
|
224
|
+
const generator = htmlGenerator`<div>!${generatorExample2()}</div>`;
|
|
225
|
+
let accumulator = "";
|
|
226
|
+
|
|
227
|
+
for (const value of generator) {
|
|
228
|
+
accumulator += value;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
assert.strictEqual(accumulator, "<div><p></div>");
|
|
232
|
+
assert.strictEqual(generator.next().done, true);
|
|
233
|
+
});
|
|
234
|
+
|
|
207
235
|
test("htmlGenerator works with other generators (escaped)", () => {
|
|
208
236
|
const generator = htmlGenerator`<div>${generatorExample()}</div>`;
|
|
209
237
|
let accumulator = "";
|
|
@@ -291,6 +319,17 @@ test("htmlAsyncGenerator works with other generators (raw)", async () => {
|
|
|
291
319
|
);
|
|
292
320
|
});
|
|
293
321
|
|
|
322
|
+
test("htmlAsyncGenerator works with other generators (raw) /2", async () => {
|
|
323
|
+
const generator = htmlAsyncGenerator`<div>!${generatorPromiseExample2()}</div>`;
|
|
324
|
+
let accumulator = "";
|
|
325
|
+
|
|
326
|
+
for await (const value of generator) {
|
|
327
|
+
accumulator += value;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
assert.strictEqual(accumulator, "<div><p></div>");
|
|
331
|
+
});
|
|
332
|
+
|
|
294
333
|
test("htmlAsyncGenerator works with other generators (escaped)", async () => {
|
|
295
334
|
const generator = htmlAsyncGenerator`<div>${generatorExample()}</div>`;
|
|
296
335
|
let accumulator = "";
|
package/.eslintrc.json
DELETED
package/src/html.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export function html(literals: TemplateStringsArray, ...expressions: any[]): string;
|
|
2
|
-
export function htmlGenerator(literals: TemplateStringsArray, ...expressions: any[]): Generator<string, void, void>;
|
|
3
|
-
export function htmlAsyncGenerator(literals: TemplateStringsArray, ...expressions: any[]): AsyncGenerator<string, void, void>;
|
package/src/html.js
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
const escapeRegExp = /["&'<=>]/g;
|
|
2
|
-
|
|
3
|
-
const escapeFunction = (string) => {
|
|
4
|
-
let escaped = "";
|
|
5
|
-
let start = 0;
|
|
6
|
-
|
|
7
|
-
while (escapeRegExp.test(string)) {
|
|
8
|
-
const i = escapeRegExp.lastIndex - 1;
|
|
9
|
-
|
|
10
|
-
switch (string.charCodeAt(i)) {
|
|
11
|
-
case 34: // "
|
|
12
|
-
escaped += string.slice(start, i) + """;
|
|
13
|
-
break;
|
|
14
|
-
case 38: // &
|
|
15
|
-
escaped += string.slice(start, i) + "&";
|
|
16
|
-
break;
|
|
17
|
-
case 39: // '
|
|
18
|
-
escaped += string.slice(start, i) + "'";
|
|
19
|
-
break;
|
|
20
|
-
case 60: // <
|
|
21
|
-
escaped += string.slice(start, i) + "<";
|
|
22
|
-
break;
|
|
23
|
-
case 61: // =
|
|
24
|
-
escaped += string.slice(start, i) + "=";
|
|
25
|
-
break;
|
|
26
|
-
case 62: // >
|
|
27
|
-
escaped += string.slice(start, i) + ">";
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
start = escapeRegExp.lastIndex;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return escaped + string.slice(start);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* The `html` function is designed to tag template literals and automatically escape their expressions.
|
|
39
|
-
* @param {TemplateStringsArray} literals Tagged template literals.
|
|
40
|
-
* @param {...any} expressions Expressions to interpolate.
|
|
41
|
-
* @returns {string} The processed HTML string.
|
|
42
|
-
*/
|
|
43
|
-
export const html = (literals, ...expressions) => {
|
|
44
|
-
let accumulator = "";
|
|
45
|
-
|
|
46
|
-
for (let i = 0; i !== expressions.length; ++i) {
|
|
47
|
-
let literal = literals.raw[i];
|
|
48
|
-
let string =
|
|
49
|
-
typeof expressions[i] === "string"
|
|
50
|
-
? expressions[i]
|
|
51
|
-
: expressions[i] == null
|
|
52
|
-
? ""
|
|
53
|
-
: Array.isArray(expressions[i])
|
|
54
|
-
? expressions[i].join("")
|
|
55
|
-
: `${expressions[i]}`;
|
|
56
|
-
|
|
57
|
-
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
58
|
-
literal = literal.slice(0, -1);
|
|
59
|
-
} else {
|
|
60
|
-
string &&= escapeFunction(string);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
accumulator += literal + string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return accumulator + literals.raw[expressions.length];
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* The `htmlGenerator` function acts as the generator version of the `html` function.
|
|
71
|
-
* @param {TemplateStringsArray} literals Tagged template literals.
|
|
72
|
-
* @param {...any} expressions Expressions to interpolate.
|
|
73
|
-
* @yields Processed HTML strings.
|
|
74
|
-
* @returns {Generator<string, void, void>} The HTML generator.
|
|
75
|
-
*/
|
|
76
|
-
export const htmlGenerator = function* (literals, ...expressions) {
|
|
77
|
-
for (let i = 0; i !== expressions.length; ++i) {
|
|
78
|
-
let expression = expressions[i];
|
|
79
|
-
let literal = literals.raw[i];
|
|
80
|
-
let string;
|
|
81
|
-
|
|
82
|
-
if (typeof expression === "string") {
|
|
83
|
-
string = expression;
|
|
84
|
-
} else if (expression == null) {
|
|
85
|
-
string = "";
|
|
86
|
-
} else {
|
|
87
|
-
if (expression[Symbol.iterator]) {
|
|
88
|
-
const isRaw =
|
|
89
|
-
literal !== "" && literal.charCodeAt(literal.length - 1) === 33;
|
|
90
|
-
|
|
91
|
-
if (isRaw) {
|
|
92
|
-
literal = literal.slice(0, -1);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (literal) {
|
|
96
|
-
yield literal;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
for (expression of expression) {
|
|
100
|
-
if (typeof expression === "string") {
|
|
101
|
-
string = expression;
|
|
102
|
-
} else {
|
|
103
|
-
if (expression == null) {
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (expression[Symbol.iterator]) {
|
|
108
|
-
for (expression of expression) {
|
|
109
|
-
if (typeof expression === "string") {
|
|
110
|
-
string = expression;
|
|
111
|
-
} else {
|
|
112
|
-
if (expression == null) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
string = `${expression}`;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (string) {
|
|
120
|
-
if (!isRaw) {
|
|
121
|
-
string = escapeFunction(string);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
yield string;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
string = `${expression}`;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (string) {
|
|
135
|
-
if (!isRaw) {
|
|
136
|
-
string = escapeFunction(string);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
yield string;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
string = `${expression}`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
150
|
-
literal = literal.slice(0, -1);
|
|
151
|
-
} else {
|
|
152
|
-
string &&= escapeFunction(string);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (literal || string) {
|
|
156
|
-
yield literal + string;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (literals.raw[expressions.length]) {
|
|
161
|
-
yield literals.raw[expressions.length];
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* This version of HTML generator should be preferred for asynchronous and streaming use cases.
|
|
167
|
-
* @param {TemplateStringsArray} literals Tagged template literals.
|
|
168
|
-
* @param {...any} expressions Expressions to interpolate.
|
|
169
|
-
* @yields Processed HTML strings.
|
|
170
|
-
* @returns {AsyncGenerator<string, void, void>} The HTML generator.
|
|
171
|
-
*/
|
|
172
|
-
export const htmlAsyncGenerator = async function* (literals, ...expressions) {
|
|
173
|
-
for (let i = 0; i !== expressions.length; ++i) {
|
|
174
|
-
let expression = await expressions[i];
|
|
175
|
-
let literal = literals.raw[i];
|
|
176
|
-
let string;
|
|
177
|
-
|
|
178
|
-
if (typeof expression === "string") {
|
|
179
|
-
string = expression;
|
|
180
|
-
} else if (expression == null) {
|
|
181
|
-
string = "";
|
|
182
|
-
} else {
|
|
183
|
-
if (expression[Symbol.iterator] || expression[Symbol.asyncIterator]) {
|
|
184
|
-
const isRaw =
|
|
185
|
-
literal !== "" && literal.charCodeAt(literal.length - 1) === 33;
|
|
186
|
-
|
|
187
|
-
if (isRaw) {
|
|
188
|
-
literal = literal.slice(0, -1);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (literal) {
|
|
192
|
-
yield literal;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
for await (expression of expression) {
|
|
196
|
-
if (typeof expression === "string") {
|
|
197
|
-
string = expression;
|
|
198
|
-
} else {
|
|
199
|
-
if (expression == null) {
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
expression[Symbol.iterator] ||
|
|
205
|
-
expression[Symbol.asyncIterator]
|
|
206
|
-
) {
|
|
207
|
-
for await (expression of expression) {
|
|
208
|
-
if (typeof expression === "string") {
|
|
209
|
-
string = expression;
|
|
210
|
-
} else {
|
|
211
|
-
if (expression == null) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
string = `${expression}`;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (string) {
|
|
219
|
-
if (!isRaw) {
|
|
220
|
-
string = escapeFunction(string);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
yield string;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
string = `${expression}`;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (string) {
|
|
234
|
-
if (!isRaw) {
|
|
235
|
-
string = escapeFunction(string);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
yield string;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
string = `${expression}`;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
|
|
249
|
-
literal = literal.slice(0, -1);
|
|
250
|
-
} else {
|
|
251
|
-
string &&= escapeFunction(string);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (literal || string) {
|
|
255
|
-
yield literal + string;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (literals.raw[expressions.length]) {
|
|
260
|
-
yield literals.raw[expressions.length];
|
|
261
|
-
}
|
|
262
|
-
};
|