@daz4126/swifty 2.1.0 → 2.2.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 +101 -48
- 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,7 +119,7 @@ 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);
|
|
@@ -106,7 +128,6 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
|
|
|
106
128
|
|
|
107
129
|
// Await all directory recursion
|
|
108
130
|
await Promise.all(directoryPromises);
|
|
109
|
-
|
|
110
131
|
} catch (err) {
|
|
111
132
|
console.error("Error reading directory:", err);
|
|
112
133
|
}
|
|
@@ -121,23 +142,29 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
|
|
|
121
142
|
name: "Tags",
|
|
122
143
|
title: "All Tags",
|
|
123
144
|
layout: "pages",
|
|
124
|
-
updated_at: new Date().toLocaleDateString(
|
|
145
|
+
updated_at: new Date().toLocaleDateString(
|
|
146
|
+
undefined,
|
|
147
|
+
defaultConfig.dateFormat,
|
|
148
|
+
),
|
|
125
149
|
data: { ...config },
|
|
126
|
-
pages: []
|
|
150
|
+
pages: [],
|
|
127
151
|
};
|
|
128
152
|
|
|
129
153
|
for (const [tag, pagesForTag] of tagsMap) {
|
|
130
154
|
const page = {
|
|
131
155
|
name: tag,
|
|
132
156
|
title: tag,
|
|
133
|
-
updated_at: new Date().toLocaleDateString(
|
|
157
|
+
updated_at: new Date().toLocaleDateString(
|
|
158
|
+
undefined,
|
|
159
|
+
defaultConfig.dateFormat,
|
|
160
|
+
),
|
|
134
161
|
url: `/tags/${tag}`,
|
|
135
162
|
layout: tagLayout ? "tags" : "pages",
|
|
136
163
|
data: { ...config, title: `Pages tagged with ${capitalize(tag)}` },
|
|
137
164
|
};
|
|
138
165
|
page.content = pagesForTag
|
|
139
|
-
.map(p => `* <a href="${p.url}">${p.title}</a>`)
|
|
140
|
-
.join(
|
|
166
|
+
.map((p) => `* <a href="${p.url}">${p.title}</a>`)
|
|
167
|
+
.join("\n");
|
|
141
168
|
|
|
142
169
|
tagPage.pages.push(page);
|
|
143
170
|
}
|
|
@@ -148,8 +175,7 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
|
|
|
148
175
|
return pages;
|
|
149
176
|
};
|
|
150
177
|
|
|
151
|
-
|
|
152
|
-
const generateLinkList = async (name,pages) => {
|
|
178
|
+
const generateLinkList = async (name, pages) => {
|
|
153
179
|
const partial = `${name}.md`;
|
|
154
180
|
const partialPath = path.join(dirs.partials, partial);
|
|
155
181
|
const linksPath = path.join(dirs.partials, defaultConfig.default_link_name);
|
|
@@ -157,52 +183,79 @@ const generateLinkList = async (name,pages) => {
|
|
|
157
183
|
const fileExists = await fsExtra.pathExists(partialPath);
|
|
158
184
|
const defaultExists = await fsExtra.pathExists(linksPath);
|
|
159
185
|
if (fileExists || defaultExists) {
|
|
160
|
-
const partial = await fs.readFile(
|
|
161
|
-
|
|
162
|
-
|
|
186
|
+
const partial = await fs.readFile(
|
|
187
|
+
fileExists ? partialPath : linksPath,
|
|
188
|
+
"utf-8",
|
|
189
|
+
);
|
|
190
|
+
const content = await Promise.all(
|
|
191
|
+
pages.map((page) => replacePlaceholders(partial, page)),
|
|
192
|
+
);
|
|
193
|
+
return content.join("\n");
|
|
163
194
|
} else {
|
|
164
|
-
return `${pages.map(page => `<li><a href="${page.url}" class="${defaultConfig.link_class}">${page.title}</a></li>`).join`\n`}
|
|
195
|
+
return `${pages.map((page) => `<li><a href="${page.url}" class="${defaultConfig.link_class}">${page.title}</a></li>`).join`\n`}`;
|
|
165
196
|
}
|
|
166
197
|
};
|
|
167
198
|
|
|
168
|
-
const render = async page => {
|
|
169
|
-
const
|
|
199
|
+
const render = async (page) => {
|
|
200
|
+
const replacedContent = await replacePlaceholders(page.content, page);
|
|
201
|
+
const htmlContent = marked.parse(replacedContent); // Markdown processed once
|
|
170
202
|
const wrappedContent = await applyLayoutAndWrapContent(page, htmlContent);
|
|
171
|
-
const htmlWithTemplate = template.replace(
|
|
203
|
+
const htmlWithTemplate = template.replace(
|
|
204
|
+
/\{\{\s*content\s*\}\}/g,
|
|
205
|
+
wrappedContent,
|
|
206
|
+
);
|
|
172
207
|
const finalContent = await replacePlaceholders(htmlWithTemplate, page);
|
|
173
208
|
return finalContent;
|
|
174
209
|
};
|
|
175
210
|
|
|
176
211
|
const createPages = async (pages, distDir = dirs.dist) => {
|
|
177
|
-
await Promise.all(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}));
|
|
212
|
+
await Promise.all(
|
|
213
|
+
pages.map(async (page) => {
|
|
214
|
+
const html = await render(page);
|
|
215
|
+
const pageDir = path.join(distDir, page.url);
|
|
216
|
+
const pagePath = path.join(distDir, page.url, "index.html");
|
|
217
|
+
await fsExtra.ensureDir(pageDir);
|
|
218
|
+
await fs.writeFile(pagePath, html);
|
|
219
|
+
if (page.folder) {
|
|
220
|
+
await createPages(page.pages, distDir); // Recursive still needs to await
|
|
221
|
+
}
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
190
224
|
};
|
|
191
225
|
|
|
192
226
|
const addLinks = async (pages, parent) => {
|
|
193
227
|
for (const page of pages) {
|
|
194
228
|
page.data ||= {};
|
|
195
229
|
page.data.links_to_tags = page?.data?.tags?.length
|
|
196
|
-
? page.data.tags.map(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
230
|
+
? page.data.tags.map(
|
|
231
|
+
(tag) =>
|
|
232
|
+
`<a class="${defaultConfig.tag_class}" href="/tags/${tag}">${tag}</a>`,
|
|
233
|
+
).join``
|
|
234
|
+
: "";
|
|
235
|
+
const crumb = page.root
|
|
236
|
+
? ""
|
|
237
|
+
: ` ${defaultConfig.breadcrumb_separator} <a class="${defaultConfig.breadcrumb_class}" href="${page.url}">${page.name}</a>`;
|
|
238
|
+
page.data.breadcrumbs = parent
|
|
239
|
+
? parent.data.breadcrumbs + crumb
|
|
240
|
+
: `<a class="${defaultConfig.breadcrumb_class}" href="/">Home</a>` +
|
|
241
|
+
crumb;
|
|
242
|
+
page.data.links_to_children = page.pages
|
|
243
|
+
? await generateLinkList(page.filename, page.pages)
|
|
244
|
+
: "";
|
|
245
|
+
page.data.links_to_siblings = await generateLinkList(
|
|
246
|
+
page.parent?.filename || "pages",
|
|
247
|
+
pages.filter((p) => p.url !== page.url),
|
|
248
|
+
);
|
|
249
|
+
page.data.links_to_self_and_siblings = await generateLinkList(
|
|
250
|
+
page.parent?.filename || "pages",
|
|
251
|
+
pages,
|
|
252
|
+
);
|
|
253
|
+
page.data.nav_links = await generateLinkList(
|
|
254
|
+
"nav",
|
|
255
|
+
pageIndex.filter((p) => p.nav),
|
|
256
|
+
);
|
|
204
257
|
if (page.pages) {
|
|
205
|
-
await addLinks(page.pages, page);
|
|
258
|
+
await addLinks(page.pages, page); // Recursive call
|
|
206
259
|
}
|
|
207
260
|
}
|
|
208
261
|
};
|
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 in ${outDir}...`);
|
|
43
|
+
}
|