@daz4126/swifty 2.1.0 → 2.3.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/package.json +1 -1
- package/src/assets.js +74 -49
- package/src/build.js +4 -6
- package/src/cli.js +48 -20
- package/src/pages.js +105 -53
- package/src/partials.js +30 -20
- package/src/watcher.js +35 -31
package/package.json
CHANGED
package/src/assets.js
CHANGED
|
@@ -5,10 +5,10 @@ import sharp from "sharp";
|
|
|
5
5
|
import { dirs, defaultConfig } from "./config.js";
|
|
6
6
|
|
|
7
7
|
const validExtensions = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
css: [".css"],
|
|
9
|
+
js: [".js"],
|
|
10
|
+
images: [".png", ".jpg", ".jpeg", ".gif", ".svg", " .webp"],
|
|
11
|
+
};
|
|
12
12
|
|
|
13
13
|
const ensureAndCopy = async (source, destination, validExts) => {
|
|
14
14
|
if (await fsExtra.pathExists(source)) {
|
|
@@ -17,64 +17,89 @@ const ensureAndCopy = async (source, destination, validExts) => {
|
|
|
17
17
|
const files = await fs.readdir(source);
|
|
18
18
|
await Promise.all(
|
|
19
19
|
files
|
|
20
|
-
.filter(file => validExts.includes(path.extname(file).toLowerCase()))
|
|
21
|
-
.map(
|
|
20
|
+
.filter((file) => validExts.includes(path.extname(file).toLowerCase()))
|
|
21
|
+
.map((file) =>
|
|
22
|
+
fsExtra.copy(path.join(source, file), path.join(destination, file)),
|
|
23
|
+
),
|
|
22
24
|
);
|
|
23
25
|
console.log(`Copied valid files from ${source} to ${destination}`);
|
|
24
26
|
} else {
|
|
25
27
|
console.log(`No ${path.basename(source)} found in ${source}`);
|
|
26
28
|
}
|
|
27
29
|
};
|
|
28
|
-
const copyAssets = async () => {
|
|
29
|
-
await ensureAndCopy(
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const copyAssets = async (outputDir = dirs.dist) => {
|
|
31
|
+
await ensureAndCopy(
|
|
32
|
+
dirs.css,
|
|
33
|
+
path.join(outputDir, "css"),
|
|
34
|
+
validExtensions.css,
|
|
35
|
+
);
|
|
36
|
+
await ensureAndCopy(dirs.js, path.join(outputDir, "js"), validExtensions.js);
|
|
37
|
+
await ensureAndCopy(
|
|
38
|
+
dirs.images,
|
|
39
|
+
path.join(outputDir, "images"),
|
|
40
|
+
validExtensions.images,
|
|
41
|
+
);
|
|
32
42
|
};
|
|
33
|
-
async function optimizeImages() {
|
|
43
|
+
async function optimizeImages(outputDir = dirs.dist) {
|
|
34
44
|
try {
|
|
35
|
-
const IMAGE_EXTENSIONS = [
|
|
36
|
-
const images_folder = path.join(
|
|
45
|
+
const IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png"];
|
|
46
|
+
const images_folder = path.join(outputDir, "images");
|
|
37
47
|
const files = await fs.readdir(images_folder);
|
|
38
|
-
|
|
39
|
-
await Promise.all(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
.
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
|
|
49
|
+
await Promise.all(
|
|
50
|
+
files.map(async (file) => {
|
|
51
|
+
const filePath = path.join(images_folder, file);
|
|
52
|
+
const ext = path.extname(file).toLowerCase();
|
|
53
|
+
|
|
54
|
+
if (!IMAGE_EXTENSIONS.includes(ext)) return;
|
|
55
|
+
|
|
56
|
+
const optimizedPath = path.join(
|
|
57
|
+
images_folder,
|
|
58
|
+
`${path.basename(file, ext)}.webp`,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (filePath !== optimizedPath) {
|
|
62
|
+
const image = sharp(filePath);
|
|
63
|
+
const metadata = await image.metadata();
|
|
64
|
+
const originalWidth = metadata.width || 0;
|
|
65
|
+
const maxWidth = defaultConfig.max_image_size || 800;
|
|
66
|
+
const resizeWidth = Math.min(originalWidth, maxWidth);
|
|
67
|
+
|
|
68
|
+
await image
|
|
69
|
+
.resize({ width: resizeWidth })
|
|
70
|
+
.toFormat("webp", { quality: 80 })
|
|
71
|
+
.toFile(optimizedPath);
|
|
72
|
+
|
|
73
|
+
await fs.unlink(filePath);
|
|
74
|
+
|
|
75
|
+
console.log(`Optimized ${file} -> ${optimizedPath}`);
|
|
76
|
+
}
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
64
79
|
} catch (error) {
|
|
65
|
-
console.error(
|
|
80
|
+
console.error("Error optimizing images:", error);
|
|
66
81
|
}
|
|
67
|
-
}
|
|
82
|
+
}
|
|
68
83
|
const generateAssetImports = async (dir, tagTemplate, validExts) => {
|
|
69
|
-
if (!(await fsExtra.pathExists(dir))) return
|
|
84
|
+
if (!(await fsExtra.pathExists(dir))) return "";
|
|
70
85
|
const files = await fs.readdir(dir);
|
|
71
86
|
return files
|
|
72
|
-
.filter(file => validExts.includes(path.extname(file).toLowerCase()))
|
|
73
|
-
.sort()
|
|
74
|
-
.map(file => tagTemplate(file))
|
|
75
|
-
.join(
|
|
87
|
+
.filter((file) => validExts.includes(path.extname(file).toLowerCase()))
|
|
88
|
+
.sort()
|
|
89
|
+
.map((file) => tagTemplate(file))
|
|
90
|
+
.join("\n");
|
|
76
91
|
};
|
|
77
|
-
const getCssImports = () =>
|
|
78
|
-
|
|
92
|
+
const getCssImports = () =>
|
|
93
|
+
generateAssetImports(
|
|
94
|
+
dirs.css,
|
|
95
|
+
(file) => `<link rel="stylesheet" href="/css/${file}" />`,
|
|
96
|
+
validExtensions.css,
|
|
97
|
+
);
|
|
98
|
+
const getJsImports = () =>
|
|
99
|
+
generateAssetImports(
|
|
100
|
+
dirs.js,
|
|
101
|
+
(file) => `<script src="/js/${file}"></script>`,
|
|
102
|
+
validExtensions.js,
|
|
103
|
+
);
|
|
79
104
|
|
|
80
|
-
export { copyAssets, optimizeImages, getCssImports, getJsImports };
|
|
105
|
+
export { copyAssets, optimizeImages, getCssImports, getJsImports };
|
package/src/build.js
CHANGED
|
@@ -2,12 +2,10 @@ import { copyAssets, optimizeImages } from "./assets.js";
|
|
|
2
2
|
import { generatePages, createPages, addLinks } from "./pages.js";
|
|
3
3
|
import { dirs } from "./config.js";
|
|
4
4
|
|
|
5
|
-
async function
|
|
6
|
-
await copyAssets();
|
|
7
|
-
await optimizeImages();
|
|
5
|
+
export default async function build(outputDir) {
|
|
6
|
+
await copyAssets(outputDir);
|
|
7
|
+
await optimizeImages(outputDir);
|
|
8
8
|
const pages = await generatePages(dirs.pages);
|
|
9
9
|
await addLinks(pages);
|
|
10
|
-
await createPages(pages);
|
|
10
|
+
await createPages(pages, outputDir);
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
buildSite();
|
package/src/cli.js
CHANGED
|
@@ -1,32 +1,60 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
|
|
3
5
|
const args = process.argv.slice(2);
|
|
4
|
-
|
|
5
|
-
let outDir = 'dist'; // default
|
|
6
|
+
let outDir = "dist"; // default
|
|
6
7
|
|
|
7
8
|
// Look for --out [folder]
|
|
8
|
-
const outIndex = args.indexOf(
|
|
9
|
+
const outIndex = args.indexOf("--out");
|
|
9
10
|
if (outIndex !== -1 && args[outIndex + 1]) {
|
|
10
11
|
outDir = args[outIndex + 1];
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
// Pass outDir as an environment variable
|
|
14
|
+
// Pass outDir as an environment variable too (optional, still useful)
|
|
14
15
|
process.env.OUT_DIR = outDir;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
17
|
+
async function main() {
|
|
18
|
+
const command = args[0];
|
|
19
|
+
|
|
20
|
+
switch (command) {
|
|
21
|
+
case "init":
|
|
22
|
+
await import("./init.js");
|
|
23
|
+
break;
|
|
24
|
+
case "build": {
|
|
25
|
+
const build = await import("./build.js");
|
|
26
|
+
if (typeof build.default === "function") {
|
|
27
|
+
await build.default(outDir);
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case "start": {
|
|
32
|
+
const build = await import("./build.js");
|
|
33
|
+
if (typeof build.default === "function") {
|
|
34
|
+
await build.default(outDir);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Run watcher.js (non-blocking)
|
|
38
|
+
const watcher = await import("./watcher.js");
|
|
39
|
+
if (typeof watcher.default === "function") {
|
|
40
|
+
watcher.default(outDir);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Start the server (blocking)
|
|
44
|
+
spawn("npx", ["serve", outDir], { stdio: "inherit" });
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "watch": {
|
|
48
|
+
const watch = await import("./watcher.js");
|
|
49
|
+
if (typeof watch.default === "function") {
|
|
50
|
+
await watch.default(outDir);
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
default:
|
|
55
|
+
console.log(`Unknown command: ${command}`);
|
|
56
|
+
console.log(`Usage: swifty [init|build|start|watch] [--out folder]`);
|
|
57
|
+
}
|
|
32
58
|
}
|
|
59
|
+
|
|
60
|
+
main();
|
package/src/pages.js
CHANGED
|
@@ -11,7 +11,7 @@ import path from "path";
|
|
|
11
11
|
const isValid = async (filePath) => {
|
|
12
12
|
try {
|
|
13
13
|
const stats = await fs.stat(filePath);
|
|
14
|
-
return stats.isDirectory() || path.extname(filePath) ===
|
|
14
|
+
return stats.isDirectory() || path.extname(filePath) === ".md";
|
|
15
15
|
} catch (err) {
|
|
16
16
|
return false; // Handle errors like file not found
|
|
17
17
|
}
|
|
@@ -43,24 +43,46 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
|
|
|
43
43
|
const root = file.name === "index.md" && !parent;
|
|
44
44
|
const relativePath = path.relative(baseDir, filePath).replace(/\\/g, "/");
|
|
45
45
|
const finalPath = `/${relativePath.replace(/\.md$/, "")}`;
|
|
46
|
-
const name = root
|
|
46
|
+
const name = root
|
|
47
|
+
? "Home"
|
|
48
|
+
: capitalize(file.name.replace(/\.md$/, "").replace(/-/g, " "));
|
|
47
49
|
const stats = await fs.stat(filePath);
|
|
48
50
|
const isDirectory = file.isDirectory();
|
|
49
|
-
const layoutFileExists =
|
|
50
|
-
|
|
51
|
+
const layoutFileExists =
|
|
52
|
+
parent &&
|
|
53
|
+
(await fsExtra.pathExists(`${dirs.layouts}/${parent.filename}.html`));
|
|
54
|
+
const layout = layoutFileExists
|
|
55
|
+
? parent.filename
|
|
56
|
+
: parent
|
|
57
|
+
? parent.layout
|
|
58
|
+
: config.default_layout_name;
|
|
51
59
|
|
|
52
60
|
const page = {
|
|
53
|
-
name,
|
|
61
|
+
name,
|
|
62
|
+
root,
|
|
63
|
+
layout,
|
|
64
|
+
filePath,
|
|
54
65
|
filename: file.name.replace(/\.md$/, ""),
|
|
55
66
|
url: root ? "/" : finalPath,
|
|
56
67
|
nav: !parent && !root,
|
|
57
|
-
parent: parent
|
|
68
|
+
parent: parent
|
|
69
|
+
? { title: parent.data.title, url: parent.url }
|
|
70
|
+
: undefined,
|
|
58
71
|
folder: isDirectory,
|
|
59
72
|
title: name,
|
|
60
|
-
created_at: new Date(stats.birthtime).toLocaleDateString(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
created_at: new Date(stats.birthtime).toLocaleDateString(
|
|
74
|
+
undefined,
|
|
75
|
+
config.dateFormat,
|
|
76
|
+
),
|
|
77
|
+
updated_at: new Date(stats.mtime).toLocaleDateString(
|
|
78
|
+
undefined,
|
|
79
|
+
config.dateFormat,
|
|
80
|
+
),
|
|
81
|
+
date: new Date(stats.mtime).toLocaleDateString(
|
|
82
|
+
undefined,
|
|
83
|
+
config.dateFormat,
|
|
84
|
+
),
|
|
85
|
+
data: root ? { ...defaultConfig } : { ...config },
|
|
64
86
|
};
|
|
65
87
|
|
|
66
88
|
if (path.extname(file.name) === ".md") {
|
|
@@ -97,16 +119,17 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
|
|
|
97
119
|
|
|
98
120
|
// Add tags
|
|
99
121
|
if (page.data.tags) {
|
|
100
|
-
page.data.tags.forEach(tag => addToTagMap(tag, page));
|
|
122
|
+
page.data.tags.forEach((tag) => addToTagMap(tag, page));
|
|
101
123
|
}
|
|
102
124
|
|
|
103
125
|
pages.push(page);
|
|
104
|
-
pageIndex.
|
|
126
|
+
if (!pageIndex.some((p) => p.url === page.url)) {
|
|
127
|
+
pageIndex.push({ url: page.url, title: page.title, nav: page.nav });
|
|
128
|
+
}
|
|
105
129
|
});
|
|
106
130
|
|
|
107
131
|
// Await all directory recursion
|
|
108
132
|
await Promise.all(directoryPromises);
|
|
109
|
-
|
|
110
133
|
} catch (err) {
|
|
111
134
|
console.error("Error reading directory:", err);
|
|
112
135
|
}
|
|
@@ -120,36 +143,38 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
|
|
|
120
143
|
folder: true,
|
|
121
144
|
name: "Tags",
|
|
122
145
|
title: "All Tags",
|
|
123
|
-
layout: "
|
|
124
|
-
updated_at: new Date().toLocaleDateString(
|
|
146
|
+
layout: tagLayout ? "tags" : defaultConfig.default_layout_name,
|
|
147
|
+
updated_at: new Date().toLocaleDateString(
|
|
148
|
+
undefined,
|
|
149
|
+
defaultConfig.dateFormat,
|
|
150
|
+
),
|
|
125
151
|
data: { ...config },
|
|
126
|
-
pages: []
|
|
152
|
+
pages: [],
|
|
127
153
|
};
|
|
128
154
|
|
|
129
155
|
for (const [tag, pagesForTag] of tagsMap) {
|
|
130
156
|
const page = {
|
|
131
157
|
name: tag,
|
|
132
158
|
title: tag,
|
|
133
|
-
updated_at: new Date().toLocaleDateString(
|
|
159
|
+
updated_at: new Date().toLocaleDateString(
|
|
160
|
+
undefined,
|
|
161
|
+
defaultConfig.dateFormat,
|
|
162
|
+
),
|
|
134
163
|
url: `/tags/${tag}`,
|
|
135
|
-
layout: tagLayout ? "tags" :
|
|
164
|
+
layout: tagLayout ? "tags" : defaultConfig.default_layout_name,
|
|
136
165
|
data: { ...config, title: `Pages tagged with ${capitalize(tag)}` },
|
|
137
166
|
};
|
|
138
|
-
page.content = pagesForTag
|
|
139
|
-
.map(p => `* <a href="${p.url}">${p.title}</a>`)
|
|
140
|
-
.join('\n');
|
|
167
|
+
page.content = await generateLinkList("tags", pagesForTag);
|
|
141
168
|
|
|
142
169
|
tagPage.pages.push(page);
|
|
143
170
|
}
|
|
144
|
-
|
|
145
171
|
tagPage.content = await generateLinkList("tags", tagPage.pages);
|
|
146
172
|
pages.push(tagPage);
|
|
147
173
|
}
|
|
148
174
|
return pages;
|
|
149
175
|
};
|
|
150
176
|
|
|
151
|
-
|
|
152
|
-
const generateLinkList = async (name,pages) => {
|
|
177
|
+
const generateLinkList = async (name, pages) => {
|
|
153
178
|
const partial = `${name}.md`;
|
|
154
179
|
const partialPath = path.join(dirs.partials, partial);
|
|
155
180
|
const linksPath = path.join(dirs.partials, defaultConfig.default_link_name);
|
|
@@ -157,52 +182,79 @@ const generateLinkList = async (name,pages) => {
|
|
|
157
182
|
const fileExists = await fsExtra.pathExists(partialPath);
|
|
158
183
|
const defaultExists = await fsExtra.pathExists(linksPath);
|
|
159
184
|
if (fileExists || defaultExists) {
|
|
160
|
-
const partial = await fs.readFile(
|
|
161
|
-
|
|
162
|
-
|
|
185
|
+
const partial = await fs.readFile(
|
|
186
|
+
fileExists ? partialPath : linksPath,
|
|
187
|
+
"utf-8",
|
|
188
|
+
);
|
|
189
|
+
const content = await Promise.all(
|
|
190
|
+
pages.map((page) => replacePlaceholders(partial, page)),
|
|
191
|
+
);
|
|
192
|
+
return content.join("\n");
|
|
163
193
|
} else {
|
|
164
|
-
return `${pages.map(page => `<li><a href="${page.url}" class="${defaultConfig.link_class}">${page.title}</a></li>`).join`\n`}
|
|
194
|
+
return `${pages.map((page) => `<li><a href="${page.url}" class="${defaultConfig.link_class}">${page.title}</a></li>`).join`\n`}`;
|
|
165
195
|
}
|
|
166
196
|
};
|
|
167
197
|
|
|
168
|
-
const render = async page => {
|
|
169
|
-
const
|
|
198
|
+
const render = async (page) => {
|
|
199
|
+
const replacedContent = await replacePlaceholders(page.content, page);
|
|
200
|
+
const htmlContent = marked.parse(replacedContent); // Markdown processed once
|
|
170
201
|
const wrappedContent = await applyLayoutAndWrapContent(page, htmlContent);
|
|
171
|
-
const htmlWithTemplate = template.replace(
|
|
202
|
+
const htmlWithTemplate = template.replace(
|
|
203
|
+
/\{\{\s*content\s*\}\}/g,
|
|
204
|
+
wrappedContent,
|
|
205
|
+
);
|
|
172
206
|
const finalContent = await replacePlaceholders(htmlWithTemplate, page);
|
|
173
207
|
return finalContent;
|
|
174
208
|
};
|
|
175
209
|
|
|
176
210
|
const createPages = async (pages, distDir = dirs.dist) => {
|
|
177
|
-
await Promise.all(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}));
|
|
211
|
+
await Promise.all(
|
|
212
|
+
pages.map(async (page) => {
|
|
213
|
+
const html = await render(page);
|
|
214
|
+
const pageDir = path.join(distDir, page.url);
|
|
215
|
+
const pagePath = path.join(distDir, page.url, "index.html");
|
|
216
|
+
await fsExtra.ensureDir(pageDir);
|
|
217
|
+
await fs.writeFile(pagePath, html);
|
|
218
|
+
if (page.folder) {
|
|
219
|
+
await createPages(page.pages, distDir); // Recursive still needs to await
|
|
220
|
+
}
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
190
223
|
};
|
|
191
224
|
|
|
192
225
|
const addLinks = async (pages, parent) => {
|
|
193
226
|
for (const page of pages) {
|
|
194
227
|
page.data ||= {};
|
|
195
228
|
page.data.links_to_tags = page?.data?.tags?.length
|
|
196
|
-
? page.data.tags.map(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
229
|
+
? page.data.tags.map(
|
|
230
|
+
(tag) =>
|
|
231
|
+
`<a class="${defaultConfig.tag_class}" href="/tags/${tag}">${tag}</a>`,
|
|
232
|
+
).join``
|
|
233
|
+
: "";
|
|
234
|
+
const crumb = page.root
|
|
235
|
+
? ""
|
|
236
|
+
: ` ${defaultConfig.breadcrumb_separator} <a class="${defaultConfig.breadcrumb_class}" href="${page.url}">${page.name}</a>`;
|
|
237
|
+
page.data.breadcrumbs = parent
|
|
238
|
+
? parent.data.breadcrumbs + crumb
|
|
239
|
+
: `<a class="${defaultConfig.breadcrumb_class}" href="/">Home</a>` +
|
|
240
|
+
crumb;
|
|
241
|
+
page.data.links_to_children = page.pages
|
|
242
|
+
? await generateLinkList(page.filename, page.pages)
|
|
243
|
+
: "";
|
|
244
|
+
page.data.links_to_siblings = await generateLinkList(
|
|
245
|
+
page.parent?.filename || "pages",
|
|
246
|
+
pages.filter((p) => p.url !== page.url),
|
|
247
|
+
);
|
|
248
|
+
page.data.links_to_self_and_siblings = await generateLinkList(
|
|
249
|
+
page.parent?.filename || "pages",
|
|
250
|
+
pages,
|
|
251
|
+
);
|
|
252
|
+
page.data.nav_links = await generateLinkList(
|
|
253
|
+
"nav",
|
|
254
|
+
pageIndex.filter((p) => p.nav),
|
|
255
|
+
);
|
|
204
256
|
if (page.pages) {
|
|
205
|
-
await addLinks(page.pages, page);
|
|
257
|
+
await addLinks(page.pages, page); // Recursive call
|
|
206
258
|
}
|
|
207
259
|
}
|
|
208
260
|
};
|
package/src/partials.js
CHANGED
|
@@ -6,7 +6,6 @@ import { marked } from "marked";
|
|
|
6
6
|
|
|
7
7
|
const partialCache = new Map();
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
const loadPartial = async (partialName) => {
|
|
11
10
|
if (partialCache.has(partialName)) {
|
|
12
11
|
return partialCache.get(partialName);
|
|
@@ -36,28 +35,39 @@ const replacePlaceholders = async (template, values) => {
|
|
|
36
35
|
};
|
|
37
36
|
|
|
38
37
|
// Replace partial includes
|
|
39
|
-
template = await replaceAsync(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
template = await replaceAsync(
|
|
39
|
+
template,
|
|
40
|
+
partialRegex,
|
|
41
|
+
async (match, partialName) => {
|
|
42
|
+
let partialContent = await loadPartial(partialName);
|
|
43
|
+
partialContent = await replacePlaceholders(partialContent, values); // Recursive replacement
|
|
44
|
+
return marked(partialContent); // Convert Markdown to HTML
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
// Replace other placeholders **only outside of code blocks**
|
|
48
|
+
const codeBlockRegex =
|
|
49
|
+
/```[\s\S]*?```|`[^`]+`|<(pre|code)[^>]*>[\s\S]*?<\/\1>/g;
|
|
50
|
+
const codeBlocks = [];
|
|
51
|
+
template = template.replace(codeBlockRegex, (match) => {
|
|
52
|
+
codeBlocks.push(match);
|
|
53
|
+
return `{{CODE_BLOCK_${codeBlocks.length - 1}}}`; // Temporary placeholder
|
|
43
54
|
});
|
|
55
|
+
// Replace placeholders outside of code blocks
|
|
56
|
+
template = template.replace(/{{\s*([^}\s]+)\s*}}/g, (match, key) => {
|
|
57
|
+
return values.data && key in values?.data
|
|
58
|
+
? values.data[key]
|
|
59
|
+
: key in values
|
|
60
|
+
? values[key]
|
|
61
|
+
: match;
|
|
62
|
+
});
|
|
63
|
+
// Restore code blocks
|
|
64
|
+
template = template.replace(
|
|
65
|
+
/{{CODE_BLOCK_(\d+)}}/g,
|
|
66
|
+
(_, index) => codeBlocks[index],
|
|
67
|
+
);
|
|
44
68
|
// replace image extensions with optimized extension
|
|
45
69
|
template = template.replace(/\.(png|jpe?g|webp)/gi, ".webp");
|
|
46
|
-
// Replace other placeholders **only outside of code blocks**
|
|
47
|
-
const codeBlockRegex = /```[\s\S]*?```|`[^`]+`|<(pre|code)[^>]*>[\s\S]*?<\/\1>/g;
|
|
48
|
-
const codeBlocks = [];
|
|
49
|
-
template = template.replace(codeBlockRegex, match => {
|
|
50
|
-
codeBlocks.push(match);
|
|
51
|
-
return `{{CODE_BLOCK_${codeBlocks.length - 1}}}`; // Temporary placeholder
|
|
52
|
-
});
|
|
53
|
-
// Replace placeholders outside of code blocks
|
|
54
|
-
template = template.replace(/{{\s*([^}\s]+)\s*}}/g, (match, key) => {
|
|
55
|
-
return(values.data && key in values?.data ? values.data[key] : key in values ? values[key] : match)
|
|
56
|
-
});
|
|
57
|
-
// Restore code blocks
|
|
58
|
-
template = template.replace(/{{CODE_BLOCK_(\d+)}}/g, (_, index) => codeBlocks[index]);
|
|
59
|
-
|
|
60
70
|
return template;
|
|
61
71
|
};
|
|
62
72
|
|
|
63
|
-
export { loadPartial, replacePlaceholders };
|
|
73
|
+
export { loadPartial, replacePlaceholders };
|
package/src/watcher.js
CHANGED
|
@@ -1,39 +1,43 @@
|
|
|
1
|
-
import chokidar from
|
|
2
|
-
import { exec } from 'child_process';
|
|
1
|
+
import chokidar from "chokidar";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
const filesToWatch = [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
export default async function startWatcher(outDir = "dist") {
|
|
4
|
+
const filesToWatch = [
|
|
5
|
+
"pages/**/*.{md,html}",
|
|
6
|
+
"layouts/**/*.{html}",
|
|
7
|
+
"images/**/*",
|
|
8
|
+
"css/**/*.{css}",
|
|
9
|
+
"js/**/*.{js}",
|
|
10
|
+
"partials/**/*.{md,html}",
|
|
11
|
+
"template.html",
|
|
12
|
+
"config.yaml",
|
|
13
|
+
"config.yml",
|
|
14
|
+
"config.json",
|
|
14
15
|
];
|
|
15
|
-
const buildScript = 'npm run build'; // Your build script command
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
const watcher = chokidar.watch(filesToWatch, {
|
|
17
|
+
const watcher = chokidar.watch(filesToWatch, {
|
|
19
18
|
persistent: true,
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
ignoreInitial: true,
|
|
20
|
+
awaitWriteFinish: { stabilityThreshold: 100 },
|
|
21
|
+
debounceDelay: 200,
|
|
22
|
+
});
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
const buildModule = await import("./build.js");
|
|
25
|
+
const build = buildModule.default;
|
|
26
|
+
|
|
27
|
+
if (typeof build !== "function") {
|
|
28
|
+
console.error("❌ build.js does not export a default function.");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
watcher.on("change", async (path) => {
|
|
33
|
+
console.log(`📄 File changed: ${path}`);
|
|
34
|
+
try {
|
|
35
|
+
await build(outDir);
|
|
36
|
+
console.log("✅ Build completed");
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(`❌ Build failed: ${error.message}`);
|
|
34
39
|
}
|
|
35
|
-
console.log(stdout); // Output from build process
|
|
36
40
|
});
|
|
37
|
-
});
|
|
38
41
|
|
|
39
|
-
console.log(
|
|
42
|
+
console.log(`👀 Watching for changes...`);
|
|
43
|
+
}
|