Skip to content

Commit d041ff8

Browse files
committed
perf: optimize code block restoration and parameter replacement
- Replace iterative code block restoration with single-pass regex matching for improved performance - Escape special regex characters in placeholders to prevent unintended pattern matches - Refactor parameter placeholder replacement to use single regex pass instead of multiple iterations - Optimize file watcher configuration to monitor only relevant directories (docs, assets, fragments) - Add existence checks for watched directories to prevent errors on missing paths - Improve watcher stability with polling configuration and write finish thresholds - Add missing fs.existsSync import for directory validation - Fix trailing comma formatting inconsistencies across multiple function signatures
1 parent 25a862f commit d041ff8

2 files changed

Lines changed: 49 additions & 27 deletions

File tree

src/compiler.ts

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,21 @@ function protectCodeBlocks(content: string): {
4747

4848
function restoreCodeBlocks(
4949
content: string,
50-
codeBlocks: Map<string, string>
50+
codeBlocks: Map<string, string>,
5151
): string {
52-
let restored = content;
53-
codeBlocks.forEach((original, placeholder) => {
54-
restored = restored.replace(placeholder, original);
52+
// Build a single regex that matches all placeholders
53+
const placeholders = Array.from(codeBlocks.keys());
54+
if (placeholders.length === 0) return content;
55+
56+
// Escape special regex characters in placeholders
57+
const escapedPlaceholders = placeholders.map((p) =>
58+
p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
59+
);
60+
const regex = new RegExp(`(${escapedPlaceholders.join("|")})`, "g");
61+
62+
return content.replace(regex, (match) => {
63+
return codeBlocks.get(match) || match;
5564
});
56-
return restored;
5765
}
5866

5967
class LRUCache<K, V> {
@@ -209,12 +217,13 @@ async function processWrapperTags(
209217
if (templateContent) {
210218
const params = attrs["params"] ? parseParams(attrs["params"]) : {};
211219
if (params) {
212-
Object.entries(params).forEach(([key, value]) => {
213-
templateContent = templateContent!.replace(
214-
new RegExp(`{{\\s*${key}\\s*}}`, "g"),
215-
String(value)
216-
);
217-
});
220+
// Replace all parameter placeholders in a single pass
221+
templateContent = templateContent.replace(
222+
/{{\s*(\w+)\s*}}/g,
223+
(_, key) => {
224+
return String(params[key] ?? "");
225+
},
226+
);
218227
}
219228
innerContent = restoreCodeBlocks(innerContent, codeBlocks);
220229
innerContent = md.render(innerContent);
@@ -317,12 +326,13 @@ async function processSelfClosingTags(fileContent: string, filePath: string) {
317326
templateCache.set(resolvedPath, frag);
318327
}
319328
if (params) {
320-
Object.entries(params).forEach(([key, value]) => {
321-
fragmentContent = fragmentContent.replace(
322-
new RegExp(`{{\\s*${key}\\s*}}`, "g"),
323-
String(value)
324-
);
325-
});
329+
// Replace all parameter placeholders in a single pass
330+
fragmentContent = fragmentContent.replace(
331+
/{{\s*(\w+)\s*}}/g,
332+
(_, key) => {
333+
return String(params[key] ?? "");
334+
},
335+
);
326336
}
327337
replacements.push({ original: tagFull, replacement: fragmentContent });
328338
}

src/index.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
// native
1616
import { mkdir, open, readFile, rm, stat } from "fs/promises";
17+
import { existsSync } from "fs";
1718
import { extname, join, resolve } from "path";
1819
import { createReadStream } from "node:fs";
1920
import { exec } from "child_process";
@@ -124,7 +125,7 @@ function logHttpError(
124125
url: string,
125126
statusCode: number,
126127
message: string,
127-
error?: any
128+
error?: any,
128129
): void {
129130
const errorLog = {
130131
method,
@@ -135,7 +136,7 @@ function logHttpError(
135136
};
136137

137138
console.error(
138-
`[${errorLog.method} ${errorLog.url} - ${errorLog.statusCode}: ${errorLog.message}`
139+
`[${errorLog.method} ${errorLog.url} - ${errorLog.statusCode}: ${errorLog.message}`,
139140
);
140141
if (errorLog.error) {
141142
console.error("Error Details:", String(errorLog.error));
@@ -182,7 +183,7 @@ const server = http.createServer(async (req, res) => {
182183
});
183184

184185
const css_command = `npx tailwindcss -c tailwind.config.js -o "${normalizePath(
185-
join(config["build-directory"], "/bundle.css")
186+
join(config["build-directory"], "/bundle.css"),
186187
)}"`;
187188

188189
function buildCSS() {
@@ -218,7 +219,7 @@ function broadcastReload() {
218219
if (client.readyState === WebSocket.OPEN) {
219220
client.send("reload");
220221
}
221-
}
222+
},
222223
);
223224
}, 5);
224225
}
@@ -268,12 +269,23 @@ async function main() {
268269
}
269270
await buildCSS();
270271

271-
const watcher = chokidar.watch(root, {
272-
ignored: [".git", "node_modules", "**/node_modules", "**/.git"],
273-
ignoreInitial: true,
274-
ignorePermissionErrors: true,
275-
awaitWriteFinish: true,
276-
});
272+
const watcher = chokidar.watch(
273+
[
274+
config["docs-directory"],
275+
config["assets-folder"],
276+
normalizePath(join(cwd(), "fragments")),
277+
].filter((path) => path && existsSync(path)),
278+
{
279+
ignored: ["**/node_modules/**", "**/.git/**", config["build-directory"]],
280+
ignoreInitial: true,
281+
ignorePermissionErrors: true,
282+
usePolling: true,
283+
awaitWriteFinish: {
284+
stabilityThreshold: 200,
285+
pollInterval: 100,
286+
},
287+
},
288+
);
277289
const changesCompiler = async (file: string) => {
278290
if (parsing) return;
279291
try {

0 commit comments

Comments
 (0)