diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 001354b..0000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "root": true,
- "extends": ["plugin:grules/all"],
- "rules": {
- "no-await-in-loop": "off",
- "require-unicode-regexp": "off"
- }
-}
diff --git a/LICENSE b/LICENSE
index f55fa3b..fdc9ce7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 Gürgün Dayıoğlu
+Copyright (c) 2023 Gürgün Dayıoğlu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/bench/index.js b/bench/index.js
index d944dc2..321f573 100644
--- a/bench/index.js
+++ b/bench/index.js
@@ -1,10 +1,12 @@
/* eslint-disable no-unused-vars */
+
import { html } from "../src/index.js";
import { Bench } from "tinybench";
import { writeFileSync } from "node:fs";
import { Buffer } from "node:buffer";
let result = "";
+
const bench = new Bench({ time: 500 });
bench.add("simple HTML formatting", () => {
@@ -112,7 +114,7 @@ await bench.warmup();
await bench.run();
const table = bench.table();
-console.table(table);
+globalThis.console.table(table);
writeFileSync(
"bench/results.json",
diff --git a/bin/example/.eslintrc.json b/bin/example/.eslintrc.json
deleted file mode 100644
index ebcdcca..0000000
--- a/bin/example/.eslintrc.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "rules": {
- "require-await": "off",
- "n/no-missing-import": "off"
- }
-}
diff --git a/bin/example/assets/script.js b/bin/example/assets/script.js
index 7e64951..cdf6b44 100644
--- a/bin/example/assets/script.js
+++ b/bin/example/assets/script.js
@@ -1 +1 @@
-console.warn("Hello World!");
+globalThis.console.warn("Hello World!");
diff --git a/bin/example/package.json b/bin/example/package.json
index 3f9d280..bcd88c3 100644
--- a/bin/example/package.json
+++ b/bin/example/package.json
@@ -6,8 +6,8 @@
"build": "ghtml --roots=assets/ --refs=routes/ --prefix=/p/assets/"
},
"dependencies": {
- "@fastify/static": "^7.0.1",
- "fastify": "^4.26.1",
+ "@fastify/static": "^8.0.1",
+ "fastify": "^5.0.0",
"ghtml": "file:../../"
}
}
diff --git a/bin/example/routes/index.js b/bin/example/routes/index.js
index 0919685..041ae89 100644
--- a/bin/example/routes/index.js
+++ b/bin/example/routes/index.js
@@ -1,3 +1,5 @@
+/* eslint-disable n/no-missing-import, require-await */
+
import { html } from "ghtml";
export default async (fastify) => {
diff --git a/bin/example/server.js b/bin/example/server.js
index 681b000..0f2e03d 100644
--- a/bin/example/server.js
+++ b/bin/example/server.js
@@ -1,10 +1,12 @@
+/* eslint-disable n/no-missing-import */
+
import Fastify from "fastify";
const fastify = Fastify();
// Plugins
await fastify.register(import("@fastify/static"), {
- root: new URL("assets/", import.meta.url).pathname,
+ root: new globalThis.URL("assets/", import.meta.url).pathname,
prefix: "/p/assets/",
wildcard: false,
index: false,
@@ -20,5 +22,5 @@ fastify.listen({ port: 5050 }, (err, address) => {
throw err;
}
- console.warn(`Server listening at ${address}`);
+ globalThis.console.warn(`Server listening at ${address}`);
});
diff --git a/bin/src/index.js b/bin/src/index.js
index ff651c8..bdebf92 100755
--- a/bin/src/index.js
+++ b/bin/src/index.js
@@ -18,7 +18,7 @@ const parseArguments = (args) => {
}
if (!roots || !refs) {
- console.error(
+ globalThis.console.error(
'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/"]',
);
process.exit(1);
@@ -31,10 +31,10 @@ const main = async () => {
const { roots, refs, prefix } = parseArguments(process.argv.slice(2));
try {
- console.warn(`Generating hashes and updating file paths...`);
- console.warn(`Scanning files in: ${roots}`);
- console.warn(`Updating files in: ${refs}`);
- console.warn(`Using prefix: ${prefix}`);
+ globalThis.console.warn(`Generating hashes and updating file paths...`);
+ globalThis.console.warn(`Scanning files in: ${roots}`);
+ globalThis.console.warn(`Updating files in: ${refs}`);
+ globalThis.console.warn(`Using prefix: ${prefix}`);
await generateHashesAndReplace({
roots,
@@ -42,9 +42,11 @@ const main = async () => {
prefix,
});
- console.warn("Hash generation and file updates completed successfully.");
+ globalThis.console.warn(
+ "Hash generation and file updates completed successfully.",
+ );
} catch (error) {
- console.error(`Error occurred: ${error.message}`);
+ globalThis.console.error(`Error occurred: ${error.message}`);
process.exit(1);
}
};
diff --git a/bin/src/utils.js b/bin/src/utils.js
index 23e45c0..c538e3d 100644
--- a/bin/src/utils.js
+++ b/bin/src/utils.js
@@ -1,3 +1,5 @@
+/* eslint-disable no-await-in-loop */
+
import { Glob } from "glob";
import { createHash } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
@@ -44,12 +46,12 @@ const updateFilePathsWithHashes = async (
for (const [path, hash] of fileHashes) {
const fullPath = prefix + path;
const escapedPath = fullPath.replace(
- /[$()*+.?[\\\]^{|}]/g,
+ /[$()*+.?[\\\]^{|}]/gu,
String.raw`\$&`,
);
const regex = new RegExp(
`(?${escapedPath})(\\?(?[^#"'\`]*))?`,
- "g",
+ "gu",
);
content = content.replace(
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..1f1b2d3
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,3 @@
+import grules from "grules";
+
+export default [...grules];
diff --git a/package.json b/package.json
index 7227fff..0ae0a36 100644
--- a/package.json
+++ b/package.json
@@ -27,9 +27,9 @@
"devDependencies": {
"@fastify/pre-commit": "^2.1.0",
"c8": "^10.1.2",
- "grules": "^0.23.3",
+ "grules": "^0.24.2",
"tinybench": "^2.9.0",
- "typescript": ">=5.5.4"
+ "typescript": ">=5.6.2"
},
"repository": {
"type": "git",
diff --git a/src/html.js b/src/html.js
index 8c4c383..5f7c37f 100644
--- a/src/html.js
+++ b/src/html.js
@@ -1,3 +1,5 @@
+/* eslint-disable no-await-in-loop, require-unicode-regexp */
+
const escapeRegExp = /["&'<=>]/g;
const escapeFunction = (string) => {
@@ -35,29 +37,23 @@ const escapeFunction = (string) => {
};
/**
- * The `html` function is designed to tag template literals and automatically escape their expressions.
- * @param {TemplateStringsArray} literals Tagged template literals.
- * @param {...any} expressions Expressions to interpolate.
- * @returns {string} The processed HTML string.
+ * @param {TemplateStringsArray} literals literals
+ * @param {...any} expressions expressions
+ * @returns {string} string
*/
export const html = (literals, ...expressions) => {
let accumulator = "";
for (let i = 0; i !== expressions.length; ++i) {
let literal = literals.raw[i];
- let string =
- typeof expressions[i] === "string"
- ? expressions[i]
- : expressions[i] == null
- ? ""
- : Array.isArray(expressions[i])
- ? expressions[i].join("")
- : `${expressions[i]}`;
+ let string = Array.isArray(expressions[i])
+ ? expressions[i].join("")
+ : String(expressions[i] ?? "");
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
literal = literal.slice(0, -1);
} else {
- string &&= escapeFunction(string);
+ string = escapeFunction(string);
}
accumulator += literal + string;
@@ -67,11 +63,10 @@ export const html = (literals, ...expressions) => {
};
/**
- * The `htmlGenerator` function acts as the generator version of the `html` function.
- * @param {TemplateStringsArray} literals Tagged template literals.
- * @param {...any} expressions Expressions to interpolate.
- * @yields Processed HTML strings.
- * @returns {Generator} The HTML generator.
+ * @param {TemplateStringsArray} literals literals
+ * @param {...any} expressions expressions
+ * @yields {string} string
+ * @returns {Generator} Generator
*/
export const htmlGenerator = function* (literals, ...expressions) {
for (let i = 0; i !== expressions.length; ++i) {
@@ -81,7 +76,7 @@ export const htmlGenerator = function* (literals, ...expressions) {
if (typeof expression === "string") {
string = expression;
- } else if (expression == null) {
+ } else if (expression === undefined || expression === null) {
string = "";
} else {
if (expression[Symbol.iterator]) {
@@ -100,56 +95,56 @@ export const htmlGenerator = function* (literals, ...expressions) {
if (typeof expression === "string") {
string = expression;
} else {
- if (expression == null) {
+ if (expression === undefined || expression === null) {
continue;
}
if (expression[Symbol.iterator]) {
for (expression of expression) {
- if (typeof expression === "string") {
- string = expression;
- } else {
- if (expression == null) {
- continue;
- }
-
- string = `${expression}`;
+ if (expression === undefined || expression === null) {
+ continue;
}
- if (string) {
- if (!isRaw) {
- string = escapeFunction(string);
- }
+ string = String(expression);
+
+ if (!string) {
+ continue;
+ }
- yield string;
+ if (!isRaw) {
+ string = escapeFunction(string);
}
+
+ yield string;
}
continue;
}
- string = `${expression}`;
+ string = String(expression);
}
- if (string) {
- if (!isRaw) {
- string = escapeFunction(string);
- }
+ if (!string) {
+ continue;
+ }
- yield string;
+ if (!isRaw) {
+ string = escapeFunction(string);
}
+
+ yield string;
}
continue;
}
- string = `${expression}`;
+ string = String(expression);
}
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
literal = literal.slice(0, -1);
} else {
- string &&= escapeFunction(string);
+ string = escapeFunction(string);
}
if (literal || string) {
@@ -163,11 +158,10 @@ export const htmlGenerator = function* (literals, ...expressions) {
};
/**
- * This version of HTML generator should be preferred for asynchronous and streaming use cases.
- * @param {TemplateStringsArray} literals Tagged template literals.
- * @param {...any} expressions Expressions to interpolate.
- * @yields Processed HTML strings.
- * @returns {AsyncGenerator} The HTML generator.
+ * @param {TemplateStringsArray} literals literals
+ * @param {...any} expressions expressions
+ * @yields {string} string
+ * @returns {AsyncGenerator} AsyncGenerator
*/
export const htmlAsyncGenerator = async function* (literals, ...expressions) {
for (let i = 0; i !== expressions.length; ++i) {
@@ -177,7 +171,7 @@ export const htmlAsyncGenerator = async function* (literals, ...expressions) {
if (typeof expression === "string") {
string = expression;
- } else if (expression == null) {
+ } else if (expression === undefined || expression === null) {
string = "";
} else {
if (expression[Symbol.iterator] || expression[Symbol.asyncIterator]) {
@@ -196,7 +190,7 @@ export const htmlAsyncGenerator = async function* (literals, ...expressions) {
if (typeof expression === "string") {
string = expression;
} else {
- if (expression == null) {
+ if (expression === undefined || expression === null) {
continue;
}
@@ -205,50 +199,50 @@ export const htmlAsyncGenerator = async function* (literals, ...expressions) {
expression[Symbol.asyncIterator]
) {
for await (expression of expression) {
- if (typeof expression === "string") {
- string = expression;
- } else {
- if (expression == null) {
- continue;
- }
-
- string = `${expression}`;
+ if (expression === undefined || expression === null) {
+ continue;
}
- if (string) {
- if (!isRaw) {
- string = escapeFunction(string);
- }
+ string = String(expression);
- yield string;
+ if (!string) {
+ continue;
}
+
+ if (!isRaw) {
+ string = escapeFunction(string);
+ }
+
+ yield string;
}
continue;
}
- string = `${expression}`;
+ string = String(expression);
}
- if (string) {
- if (!isRaw) {
- string = escapeFunction(string);
- }
+ if (!string) {
+ continue;
+ }
- yield string;
+ if (!isRaw) {
+ string = escapeFunction(string);
}
+
+ yield string;
}
continue;
}
- string = `${expression}`;
+ string = String(expression);
}
if (literal && literal.charCodeAt(literal.length - 1) === 33) {
literal = literal.slice(0, -1);
} else {
- string &&= escapeFunction(string);
+ string = escapeFunction(string);
}
if (literal || string) {
diff --git a/src/includeFile.js b/src/includeFile.js
index 1f69cce..1a27b80 100644
--- a/src/includeFile.js
+++ b/src/includeFile.js
@@ -3,8 +3,8 @@ import { readFileSync } from "node:fs";
const cache = new Map();
/**
- * @param {string} path The path to the file to render.
- * @returns {string} The content of the file.
+ * @param {string} path path
+ * @returns {string} string
*/
export const includeFile = (path) => {
let file = cache.get(path);
diff --git a/test/index.js b/test/index.js
index 7aa2c4d..8b467dd 100644
--- a/test/index.js
+++ b/test/index.js
@@ -22,6 +22,11 @@ const generatorExample = function* () {
yield "
";
};
+const generatorExample2 = function* () {
+ yield ["", ""];
+ yield "";
+};
+
const generatorPromiseExample = function* () {
yield [
new Promise((resolve) => {
@@ -33,6 +38,17 @@ const generatorPromiseExample = function* () {
yield;
};
+const generatorPromiseExample2 = function* () {
+ yield [
+ new Promise((resolve) => {
+ resolve("
");
+ }),
+ null,
+ "",
+ ];
+ yield "";
+};
+
test("renders empty input", () => {
assert.strictEqual(html({ raw: [""] }), "");
});
@@ -204,6 +220,18 @@ test("htmlGenerator works with other generators (raw)", () => {
assert.strictEqual(generator.next().done, true);
});
+test("htmlGenerator works with other generators (raw) /2", () => {
+ const generator = htmlGenerator`
!${generatorExample2()}
`;
+ let accumulator = "";
+
+ for (const value of generator) {
+ accumulator += value;
+ }
+
+ assert.strictEqual(accumulator, "");
+ assert.strictEqual(generator.next().done, true);
+});
+
test("htmlGenerator works with other generators (escaped)", () => {
const generator = htmlGenerator`${generatorExample()}
`;
let accumulator = "";
@@ -291,6 +319,17 @@ test("htmlAsyncGenerator works with other generators (raw)", async () => {
);
});
+test("htmlAsyncGenerator works with other generators (raw) /2", async () => {
+ const generator = htmlAsyncGenerator`!${generatorPromiseExample2()}
`;
+ let accumulator = "";
+
+ for await (const value of generator) {
+ accumulator += value;
+ }
+
+ assert.strictEqual(accumulator, "");
+});
+
test("htmlAsyncGenerator works with other generators (escaped)", async () => {
const generator = htmlAsyncGenerator`${generatorExample()}
`;
let accumulator = "";