@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daz4126/swifty",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
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
- css: [".css"],
9
- js: [".js"],
10
- images: [".png", ".jpg", ".jpeg", ".gif", ".svg"," .webp"],
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(file => fsExtra.copy(path.join(source, file), path.join(destination, file)))
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(dirs.css, path.join(dirs.dist, 'css'), validExtensions.css);
30
- await ensureAndCopy(dirs.js, path.join(dirs.dist, 'js'), validExtensions.js);
31
- await ensureAndCopy(dirs.images, path.join(dirs.dist, 'images'), validExtensions.images);
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 = ['.jpg', '.jpeg', '.png'];
36
- const images_folder = path.join(dirs.dist, "images");
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(files.map(async (file) => {
40
- const filePath = path.join(images_folder, file);
41
- const ext = path.extname(file).toLowerCase();
42
-
43
- if (!IMAGE_EXTENSIONS.includes(ext)) return;
44
-
45
- const optimizedPath = path.join(images_folder, `${path.basename(file, ext)}.webp`);
46
-
47
- if (filePath !== optimizedPath) {
48
- const image = sharp(filePath);
49
- const metadata = await image.metadata();
50
- const originalWidth = metadata.width || 0;
51
- const maxWidth = defaultConfig.max_image_size || 800;
52
- const resizeWidth = Math.min(originalWidth, maxWidth);
53
-
54
- await image
55
- .resize({ width: resizeWidth })
56
- .toFormat('webp', { quality: 80 })
57
- .toFile(optimizedPath);
58
-
59
- await fs.unlink(filePath);
60
-
61
- console.log(`Optimized ${file} -> ${optimizedPath}`);
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('Error optimizing images:', 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('\n');
87
+ .filter((file) => validExts.includes(path.extname(file).toLowerCase()))
88
+ .sort()
89
+ .map((file) => tagTemplate(file))
90
+ .join("\n");
76
91
  };
77
- const getCssImports = () => generateAssetImports(dirs.css, (file) => `<link rel="stylesheet" href="/css/${file}" />`, validExtensions.css);
78
- const getJsImports = () => generateAssetImports(dirs.js, (file) => `<script src="/js/${file}"></script>`, validExtensions.js);
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 buildSite() {
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
- const command = args[0];
5
- let outDir = 'dist'; // default
6
+ let outDir = "dist"; // default
6
7
 
7
8
  // Look for --out [folder]
8
- const outIndex = args.indexOf('--out');
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
- switch (command) {
17
- case "init":
18
- import('./init.js');
19
- break;
20
- case "build":
21
- import('./build.js');
22
- break;
23
- case "start":
24
- import('./build.js').then(async () => {
25
- const { execSync } = await import('child_process');
26
- execSync(`npx serve ${process.env.OUT_DIR}`, { stdio: 'inherit' });
27
- });
28
- break;
29
- default:
30
- console.log(`Unknown command: ${command}`);
31
- console.log(`Usage: swifty [init|build|start] [--out folder]`);
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) === '.md';
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 ? "Home" : capitalize(file.name.replace(/\.md$/, "").replace(/-/g, " "));
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 = parent && await fsExtra.pathExists(`${dirs.layouts}/${parent.filename}.html`);
50
- const layout = layoutFileExists ? parent.filename : parent ? parent.layout : config.default_layout_name;
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, root, layout, filePath,
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 ? { title: parent.data.title, url: parent.url } : undefined,
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(undefined, config.dateFormat),
61
- updated_at: new Date(stats.mtime).toLocaleDateString(undefined, config.dateFormat),
62
- date: new Date(stats.mtime).toLocaleDateString(undefined, config.dateFormat),
63
- data: root ? { ...defaultConfig } : { ...config }
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.push({ url: page.url, title: page.title, nav: page.nav });
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: "pages",
124
- updated_at: new Date().toLocaleDateString(undefined, defaultConfig.dateFormat),
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(undefined, defaultConfig.dateFormat),
159
+ updated_at: new Date().toLocaleDateString(
160
+ undefined,
161
+ defaultConfig.dateFormat,
162
+ ),
134
163
  url: `/tags/${tag}`,
135
- layout: tagLayout ? "tags" : "pages",
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(fileExists ? partialPath : linksPath, "utf-8");
161
- const content = await Promise.all(pages.map(page => replacePlaceholders(partial, page)));
162
- return content.join('\n');
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 htmlContent = marked.parse(page.content); // Markdown processed once
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(/\{\{\s*content\s*\}\}/g, wrappedContent);
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(pages.map(async (page) => {
178
- const html = await render(page);
179
- const pageDir = path.join(distDir, page.url);
180
- const pagePath = path.join(distDir, page.url, "index.html");
181
-
182
- await fsExtra.ensureDir(pageDir);
183
- await fs.writeFile(pagePath, html);
184
- console.log(`Created file: ${pagePath}`);
185
-
186
- if (page.folder) {
187
- await createPages(page.pages, distDir); // Recursive still needs to await
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(tag => `<a class="${defaultConfig.tag_class}" href="/tags/${tag}">${tag}</a>`).join`` : "";
197
- const crumb = page.root ? "" : ` ${defaultConfig.breadcrumb_separator} <a class="${defaultConfig.breadcrumb_class}" href="${page.url}">${page.name}</a>`;
198
- page.data.breadcrumbs = parent ? parent.data.breadcrumbs + crumb
199
- : `<a class="${defaultConfig.breadcrumb_class}" href="/">Home</a>` + crumb;
200
- page.data.links_to_children = page.pages ? await generateLinkList(page.filename, page.pages) : "";
201
- page.data.links_to_siblings = await generateLinkList(page.parent?.filename || "pages", pages.filter(p => p.url !== page.url));
202
- page.data.links_to_self_and_siblings = await generateLinkList(page.parent?.filename || "pages", pages);
203
- page.data.nav_links = await generateLinkList("nav", pageIndex.filter(p => p.nav));
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); // Recursive call
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(template, partialRegex, async (match, partialName) => {
40
- let partialContent = await loadPartial(partialName);
41
- partialContent = await replacePlaceholders(partialContent, values); // Recursive replacement
42
- return marked(partialContent); // Convert Markdown to HTML
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 'chokidar';
2
- import { exec } from 'child_process';
1
+ import chokidar from "chokidar";
3
2
 
4
- // Define which files to watch (you can adjust based on your project structure)
5
- const filesToWatch = [
6
- 'pages/**/*.{md,html}', // Watch JavaScript and HTML files in pages directory
7
- 'layouts/**/*.{html}', // Watch JavaScript and HTML files in layouts directory
8
- 'images/**/*', // Watch all files in images directory
9
- 'css/**/*.{css}', // Watch CSS files in css directory
10
- 'js/**/*.{js}', // Watch JS files in js directory
11
- 'partials/**/*.{md,html}', // Watch JavaScript and HTML files in partials directory
12
- 'template.html', // Watch the template HTML file
13
- 'config.yaml', 'config.yml', 'config.json' // Watch YAML and JSON config files
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
- // Initialize watcher
18
- const watcher = chokidar.watch(filesToWatch, {
17
+ const watcher = chokidar.watch(filesToWatch, {
19
18
  persistent: true,
20
- debounceDelay: 200 // Wait 200ms after the last change to trigger the build
21
- })
19
+ ignoreInitial: true,
20
+ awaitWriteFinish: { stabilityThreshold: 100 },
21
+ debounceDelay: 200,
22
+ });
22
23
 
23
- // Event listener for file changes
24
- watcher.on('change', path => {
25
- console.log(`File ${path} has been changed. Running build...`);
26
- exec(buildScript, (error, stdout, stderr) => {
27
- if (error) {
28
- console.error(`Error executing build: ${error.message}`);
29
- return;
30
- }
31
- if (stderr) {
32
- console.error(`stderr: ${stderr}`);
33
- return;
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(`Watching files for changes ...`);
42
+ console.log(`👀 Watching for changes...`);
43
+ }