@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/README.md +5 -3
- package/package.json +1 -1
- package/src/assets.js +200 -25
- package/src/build.js +11 -0
- package/src/client/IDIOMORPH-LICENSE.txt +13 -0
- package/src/client/idiomorph.esm.js +1385 -0
- package/src/client/swifty-navigation.js +399 -0
- package/src/config.js +22 -0
- package/src/init.js +11 -1
- package/src/layout.js +20 -10
- package/src/markdown.js +20 -0
- package/src/minify.js +129 -0
- package/src/pages.js +27 -13
- package/src/partials.js +50 -5
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 "
|
|
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
|
|
346
|
-
const
|
|
347
|
-
const
|
|
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 =
|
|
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
|
-
|
|
431
|
+
linkablePages.filter((p) => p.url !== page.url),
|
|
418
432
|
),
|
|
419
|
-
generateLinkList(page.parent?.filename || "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
|
-
|
|
5
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
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) => {
|