@daz4126/swifty 2.12.0 → 3.1.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/src/pages.js CHANGED
@@ -1,14 +1,17 @@
1
1
  // pages.js
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+
5
+ import fsExtra from "fs-extra";
6
+ import matter from "gray-matter";
7
+
2
8
  import { replacePlaceholders } from "./partials.js";
3
9
  import { dirs, defaultConfig, loadConfig } from "./config.js";
4
10
  import { getTemplate, applyLayoutAndWrapContent } from "./layout.js";
5
11
  import { chunkPages, generatePaginationNav } from "./pagination.js";
6
- import { marked } from "marked";
7
- import matter from "gray-matter";
8
- import fs from "fs/promises";
9
- import fsExtra from "fs-extra";
10
- import path from "path";
12
+ import { marked } from "./markdown.js";
11
13
  import { mapLimit } from "./concurrency.js";
14
+ import { minifyHtml } from "./minify.js";
12
15
 
13
16
  // Returns stats if valid (directory or .md file), null otherwise
14
17
  const getValidStats = async (filePath) => {
@@ -47,6 +50,8 @@ const pageIndexUrls = new Set();
47
50
  const partialExtensions = [".md", ".html"];
48
51
 
49
52
  const getBuildConcurrency = () => defaultConfig.build_concurrency || 16;
53
+ const minificationEnabled = () =>
54
+ defaultConfig.minify !== false && defaultConfig.minify_html !== false;
50
55
 
51
56
  const addToTagMap = (tag, page) => {
52
57
  if (!tagsMap.has(tag)) tagsMap.set(tag, []);
@@ -78,6 +83,7 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
78
83
 
79
84
  const root = file.name === "index.md" && !parent;
80
85
  const relativePath = path.relative(baseDir, filePath).replace(/\\/g, "/");
86
+ const notFound = relativePath === "404.md" && !parent;
81
87
  const finalPath = `/${relativePath.replace(/\.md$/, "")}`;
82
88
  const name = root
83
89
  ? "Home"
@@ -99,11 +105,12 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
99
105
  filePath,
100
106
  filename: file.name.replace(/\.md$/, ""),
101
107
  url: root ? "/" : finalPath,
102
- nav: !parent && !root,
108
+ nav: !parent && !root && !notFound,
103
109
  parent: parent
104
110
  ? { title: parent.meta.title, url: parent.url }
105
111
  : undefined,
106
112
  folder: isDirectory,
113
+ notFound,
107
114
  title: name,
108
115
  createdAtObj: stats.birthtime,
109
116
  updatedAtObj: stats.mtime,
@@ -126,6 +133,9 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
126
133
  const markdownContent = await fs.readFile(filePath, "utf-8");
127
134
  const { data, content } = matter(markdownContent);
128
135
  Object.assign(page, { meta: { ...page.meta, ...data }, content });
136
+ if (notFound && data.sitemap === undefined) {
137
+ page.meta.sitemap = false;
138
+ }
129
139
  page.title = page.meta.title || page.title;
130
140
  page.name = page.meta.title || page.name;
131
141
 
@@ -342,9 +352,12 @@ const createPages = async (pages, distDir = dirs.dist) => {
342
352
  await mapLimit(
343
353
  pages,
344
354
  async (page) => {
345
- const html = await render(page);
346
- const pageDir = path.join(distDir, page.url);
347
- const pagePath = path.join(distDir, page.url, "index.html");
355
+ const renderedHtml = await render(page);
356
+ const html = minificationEnabled() ? minifyHtml(renderedHtml) : renderedHtml;
357
+ const pageDir = page.notFound ? distDir : path.join(distDir, page.url);
358
+ const pagePath = page.notFound
359
+ ? path.join(distDir, "404.html")
360
+ : path.join(distDir, page.url, "index.html");
348
361
  await fsExtra.ensureDir(pageDir);
349
362
  await fs.writeFile(pagePath, html);
350
363
  if (page.folder) {
@@ -360,8 +373,9 @@ const createPages = async (pages, distDir = dirs.dist) => {
360
373
  };
361
374
 
362
375
  const addLinks = async (pages, parent) => {
376
+ const linkablePages = pages.filter((p) => !p.notFound);
363
377
  // Filter out folders and index pages for prev/next calculation (only content pages)
364
- const contentPages = pages.filter((p) => !p.folder && p.filename !== 'index');
378
+ const contentPages = linkablePages.filter((p) => !p.folder && p.filename !== 'index');
365
379
 
366
380
  await mapLimit(
367
381
  pages,
@@ -388,7 +402,7 @@ const addLinks = async (pages, parent) => {
388
402
  const linkClass = defaultConfig.prev_next_class || defaultConfig.link_class || '';
389
403
  const classAttr = linkClass ? ` class="${linkClass}"` : '';
390
404
 
391
- if (!page.folder && page.filename !== 'index') {
405
+ if (!page.folder && page.filename !== 'index' && !page.notFound) {
392
406
  const pageIndex = contentPages.indexOf(page);
393
407
  const prevSibling = pageIndex > 0 ? contentPages[pageIndex - 1] : null;
394
408
  const nextSibling = pageIndex < contentPages.length - 1 ? contentPages[pageIndex + 1] : null;
@@ -414,9 +428,9 @@ const addLinks = async (pages, parent) => {
414
428
  : Promise.resolve(""),
415
429
  generateLinkList(
416
430
  page.parent?.filename || "pages",
417
- pages.filter((p) => p.url !== page.url),
431
+ linkablePages.filter((p) => p.url !== page.url),
418
432
  ),
419
- generateLinkList(page.parent?.filename || "pages", pages),
433
+ generateLinkList(page.parent?.filename || "pages", linkablePages),
420
434
  generateLinkList("nav", pageIndex.filter((p) => p.nav)),
421
435
  ]);
422
436
 
package/src/partials.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import fs from "fs/promises";
2
- import fsExtra from "fs-extra";
3
2
  import path from "path";
4
- import { dirs, defaultConfig } from "./config.js";
5
- import { marked } from "marked";
3
+
4
+ import fsExtra from "fs-extra";
6
5
  import { Eta } from "eta";
6
+
7
+ import { dirs, defaultConfig } from "./config.js";
8
+ import { marked } from "./markdown.js";
7
9
  import { loadData } from "./data.js";
10
+ import { getResponsiveImage } from "./assets.js";
8
11
 
9
12
  const partialCache = new Map();
10
13
  const imageExtensionRegex = /\.(png|jpe?g|webp)(?=([?#]|$))/i;
@@ -51,7 +54,15 @@ const generateOgTags = (values) => {
51
54
  const description = meta.description || meta.summary || '';
52
55
  const url = values.url || '';
53
56
  const siteUrl = (meta.site_url || values.site_url || '').replace(/\/$/, '');
54
- const image = meta.image || meta.og_image || '';
57
+ // Rewrite local image paths to .webp so they match the optimized output on disk
58
+ const image = rewriteImageUrl(
59
+ meta.image ||
60
+ meta.og_image ||
61
+ meta.default_og_image ||
62
+ values.default_og_image ||
63
+ defaultConfig.default_og_image ||
64
+ '',
65
+ );
55
66
  const type = meta.og_type || (values.folder ? 'website' : 'article');
56
67
  const author = meta.author || values.author || '';
57
68
 
@@ -148,6 +159,36 @@ const rewriteSrcset = (srcset) =>
148
159
  })
149
160
  .join(", ");
150
161
 
162
+ const getAttributeValue = (tag, name) => {
163
+ const match = tag.match(new RegExp(`\\s${name}=(["'])(.*?)\\1`, "i"));
164
+ return match ? match[2] : "";
165
+ };
166
+
167
+ const hasAttribute = (tag, name) =>
168
+ new RegExp(`\\s${name}(?:=|\\s|>|/)`, "i").test(tag);
169
+
170
+ const appendAttribute = (tag, name, value) =>
171
+ tag.replace(/\s*\/?>$/, (ending) => {
172
+ const close = ending.includes("/") ? " />" : ">";
173
+ return ` ${name}="${escapeAttr(value)}"${close}`;
174
+ });
175
+
176
+ const addResponsiveImageAttributes = (tag) => {
177
+ const imageUrl = getAttributeValue(tag, "src");
178
+ const responsiveImage = getResponsiveImage(imageUrl);
179
+
180
+ if (!responsiveImage || responsiveImage.variants.length < 2) return tag;
181
+
182
+ let nextTag = tag;
183
+ if (!hasAttribute(nextTag, "srcset")) {
184
+ nextTag = appendAttribute(nextTag, "srcset", responsiveImage.srcset);
185
+ }
186
+ if (!hasAttribute(nextTag, "sizes")) {
187
+ nextTag = appendAttribute(nextTag, "sizes", responsiveImage.sizes);
188
+ }
189
+ return nextTag;
190
+ };
191
+
151
192
  const rewriteLocalImageReferences = (html) =>
152
193
  html.replace(/<([a-z][\w:-]*)(\s[^<>]*?)?>/gi, (tag, tagName) => {
153
194
  const normalizedTagName = tagName.toLowerCase();
@@ -155,7 +196,7 @@ const rewriteLocalImageReferences = (html) =>
155
196
  return tag;
156
197
  }
157
198
 
158
- return tag.replace(
199
+ const rewrittenTag = tag.replace(
159
200
  /\s(src|href|srcset)=(["'])(.*?)\2/gi,
160
201
  (attribute, attributeName, quote, value) => {
161
202
  const nextValue =
@@ -165,6 +206,10 @@ const rewriteLocalImageReferences = (html) =>
165
206
  return ` ${attributeName}=${quote}${nextValue}${quote}`;
166
207
  },
167
208
  );
209
+
210
+ return normalizedTagName === "img"
211
+ ? addResponsiveImageAttributes(rewrittenTag)
212
+ : rewrittenTag;
168
213
  });
169
214
 
170
215
  const replacePlaceholders = async (template, values) => {