@daz4126/swifty 2.10.0 → 2.12.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/init.js CHANGED
@@ -2,62 +2,52 @@
2
2
 
3
3
  import fs from "fs";
4
4
  import path from "path";
5
- import { fileURLToPath } from "url";
6
- import { dirname } from "path";
7
5
 
8
- // Needed to emulate __dirname in ES modules
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = dirname(__filename);
11
-
12
- // Define the structure
13
- const structure = {
14
- "pages/index.md": "This my home page.",
15
- "layouts/": null,
16
- "partials/": null,
17
- "css/": null,
18
- "js/": null,
19
- "images/": null,
20
- "config.yaml": `sitename: Swifty
6
+ function getStructure(sitename) {
7
+ return {
8
+ "pages/index.md": `# Welcome to ${sitename}\n\nThis is my home page.`,
9
+ "layouts/": null,
10
+ "partials/": null,
11
+ "css/": null,
12
+ "js/": null,
13
+ "images/": null,
14
+ "data/": null,
15
+ "config.yaml": `sitename: ${sitename}
21
16
  breadcrumb_separator: "»"
22
17
  breadcrumb_class: swifty_breadcrumb
23
18
  link_class: swifty_link
24
- tag_class: tag
25
- default_layout_name: site
19
+ tag_class: swifty_tag
20
+ default_layout_name: default
26
21
  default_link_name: links
27
22
  max_image_size: 800
28
23
 
29
- dateFormat:
24
+ dateFormat:
30
25
  weekday: short
31
26
  month: short
32
27
  day: numeric
33
28
  year: numeric`,
34
- "template.html": `<!DOCTYPE html>
29
+ "template.html": `<!DOCTYPE html>
35
30
  <html lang="en">
36
31
  <head>
37
32
  <meta charset="UTF-8">
38
33
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
39
- <link rel="icon" href="favicon.ico" sizes="48x48" type="image/x-icon">
40
- <link rel="icon" href="favicon-16x16.png" sizes="16x16" type="image/x-icon">
41
- <link rel="icon" href="favicon-32x32.png" sizes="32x32" type="image/png">
42
- <link rel="apple-touch-icon" href="path/to/apple-touch-icon.png">
43
- <link rel="icon" sizes="192x192" href="android-chrome-192x19.png">
44
- <link rel="icon" sizes="512x512" href="android-chrome-512x512.png">
45
- <title>{{ sitename }} || {{ title }}</title>
34
+ <title><%= sitename %> | <%= title %></title>
46
35
  </head>
47
36
  <body>
48
37
  <header>
49
- <h1>Swifty</h1>
38
+ <h1>${sitename}</h1>
50
39
  </header>
51
40
  <main>
52
- {{ content }}
41
+ <%= content %>
53
42
  </main>
54
43
  <footer>
55
44
  <p>This site was built with Swifty, the super speedy static site generator.</p>
56
45
  </footer>
57
46
  </body>
58
47
  </html>
59
- `
60
- };
48
+ `,
49
+ };
50
+ }
61
51
 
62
52
  function createStructure(basePath, structure) {
63
53
  Object.entries(structure).forEach(([filePath, content]) => {
@@ -71,7 +61,20 @@ function createStructure(basePath, structure) {
71
61
  });
72
62
  }
73
63
 
74
- const projectRoot = process.cwd();
75
- createStructure(projectRoot, structure);
64
+ export default function init(sitename) {
65
+ const projectRoot = path.join(process.cwd(), sitename);
66
+
67
+ // Check if folder already exists
68
+ if (fs.existsSync(projectRoot)) {
69
+ console.error(`Error: Folder "${sitename}" already exists.`);
70
+ process.exit(1);
71
+ }
76
72
 
77
- console.log("✅ Swifty project initialized successfully!");
73
+ const structure = getStructure(sitename);
74
+ createStructure(projectRoot, structure);
75
+
76
+ console.log(`Site "${sitename}" created successfully!`);
77
+ console.log(`\nNext steps:`);
78
+ console.log(` cd ${sitename}`);
79
+ console.log(` npx swifty start`);
80
+ }
package/src/layout.js CHANGED
@@ -40,7 +40,7 @@ const createTemplate = async () => {
40
40
  ? `<script type="module">import * as Turbo from 'https://esm.sh/@hotwired/turbo';</script>`
41
41
  : '';
42
42
  const livereloadScript = process.env.SWIFTY_WATCH
43
- ? `<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')</script>`
43
+ ? `<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':${defaultConfig.livereload_port || 35729}/livereload.js?snipver=1"></' + 'script>')</script>`
44
44
  : '';
45
45
 
46
46
  // Preload local assets for faster loading
@@ -59,10 +59,10 @@ const createTemplate = async () => {
59
59
  };
60
60
 
61
61
  const applyLayoutAndWrapContent = async (page,content) => {
62
- const layoutContent = await getLayout(page.data.layout !== undefined ? page.data.layout : page.layout);
62
+ const layoutContent = await getLayout(page.meta.layout !== undefined ? page.meta.layout : page.layout);
63
63
  if (!layoutContent) return content;
64
64
  // Use function to avoid $` special replacement patterns in content
65
- return layoutContent.replace(/\{\{\s*content\s*\}\}/g, () => content);
65
+ return layoutContent.replace(/<%=\s*content\s*%>/g, () => content);
66
66
  };
67
67
 
68
68
  const getTemplate = async () => {
package/src/pages.js CHANGED
@@ -2,11 +2,13 @@
2
2
  import { replacePlaceholders } from "./partials.js";
3
3
  import { dirs, defaultConfig, loadConfig } from "./config.js";
4
4
  import { getTemplate, applyLayoutAndWrapContent } from "./layout.js";
5
+ import { chunkPages, generatePaginationNav } from "./pagination.js";
5
6
  import { marked } from "marked";
6
7
  import matter from "gray-matter";
7
8
  import fs from "fs/promises";
8
9
  import fsExtra from "fs-extra";
9
10
  import path from "path";
11
+ import { mapLimit } from "./concurrency.js";
10
12
 
11
13
  // Returns stats if valid (directory or .md file), null otherwise
12
14
  const getValidStats = async (filePath) => {
@@ -42,6 +44,9 @@ const parseDate = (dateValue) => {
42
44
  const tagsMap = new Map();
43
45
  const pageIndex = [];
44
46
  const pageIndexUrls = new Set();
47
+ const partialExtensions = [".md", ".html"];
48
+
49
+ const getBuildConcurrency = () => defaultConfig.build_concurrency || 16;
45
50
 
46
51
  const addToTagMap = (tag, page) => {
47
52
  if (!tagsMap.has(tag)) tagsMap.set(tag, []);
@@ -58,110 +63,183 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
58
63
 
59
64
  const pages = [];
60
65
  const folderConfig = await loadConfig(sourceDir);
61
- const config = { ...defaultConfig, ...parent?.data, ...folderConfig };
66
+ const config = { ...defaultConfig, ...parent?.meta, ...folderConfig };
62
67
  const { dateFormat } = config;
63
68
 
64
69
  try {
65
70
  const files = await fs.readdir(sourceDir, { withFileTypes: true });
66
71
 
67
- // Collect promises for processing all files
68
- const filePromises = files.map(async (file) => {
69
- const filePath = path.join(sourceDir, file.name);
70
- const stats = await getValidStats(filePath);
71
- if (!stats) return null;
72
-
73
- const root = file.name === "index.md" && !parent;
74
- const relativePath = path.relative(baseDir, filePath).replace(/\\/g, "/");
75
- const finalPath = `/${relativePath.replace(/\.md$/, "")}`;
76
- const name = root
77
- ? "Home"
78
- : capitalize(file.name.replace(/\.md$/, "").replace(/-/g, " "));
79
- const isDirectory = stats.isDirectory();
80
- const layoutFileExists =
81
- parent &&
82
- (await fsExtra.pathExists(`${dirs.layouts}/${parent.filename}.html`));
83
- const layout = layoutFileExists
84
- ? parent.filename
85
- : parent
86
- ? parent.layout
87
- : config.default_layout_name;
88
-
89
- const page = {
90
- name,
91
- root,
92
- layout,
93
- filePath,
94
- filename: file.name.replace(/\.md$/, ""),
95
- url: root ? "/" : finalPath,
96
- nav: !parent && !root,
97
- parent: parent
98
- ? { title: parent.data.title, url: parent.url }
99
- : undefined,
100
- folder: isDirectory,
101
- title: name,
102
- created_at: new Date(stats.birthtime).toLocaleDateString(
103
- undefined,
104
- dateFormat,
105
- ),
106
- updated_at: new Date(stats.mtime).toLocaleDateString(
107
- undefined,
108
- dateFormat,
109
- ),
110
- date: new Date(stats.birthtime).toLocaleDateString(
111
- undefined,
112
- dateFormat,
113
- ),
114
- data: root ? { ...defaultConfig } : { ...config },
115
- };
72
+ const fileResults = await mapLimit(
73
+ files,
74
+ async (file) => {
75
+ const filePath = path.join(sourceDir, file.name);
76
+ const stats = await getValidStats(filePath);
77
+ if (!stats) return null;
78
+
79
+ const root = file.name === "index.md" && !parent;
80
+ const relativePath = path.relative(baseDir, filePath).replace(/\\/g, "/");
81
+ const finalPath = `/${relativePath.replace(/\.md$/, "")}`;
82
+ const name = root
83
+ ? "Home"
84
+ : capitalize(file.name.replace(/\.md$/, "").replace(/-/g, " "));
85
+ const isDirectory = stats.isDirectory();
86
+ const layoutFileExists =
87
+ parent &&
88
+ (await fsExtra.pathExists(`${dirs.layouts}/${parent.filename}.html`));
89
+ const layout = layoutFileExists
90
+ ? parent.filename
91
+ : parent
92
+ ? parent.layout
93
+ : config.default_layout_name;
94
+
95
+ const page = {
96
+ name,
97
+ root,
98
+ layout,
99
+ filePath,
100
+ filename: file.name.replace(/\.md$/, ""),
101
+ url: root ? "/" : finalPath,
102
+ nav: !parent && !root,
103
+ parent: parent
104
+ ? { title: parent.meta.title, url: parent.url }
105
+ : undefined,
106
+ folder: isDirectory,
107
+ title: name,
108
+ createdAtObj: stats.birthtime,
109
+ updatedAtObj: stats.mtime,
110
+ created_at: new Date(stats.birthtime).toLocaleDateString(
111
+ undefined,
112
+ dateFormat,
113
+ ),
114
+ updated_at: new Date(stats.mtime).toLocaleDateString(
115
+ undefined,
116
+ dateFormat,
117
+ ),
118
+ date: new Date(stats.birthtime).toLocaleDateString(
119
+ undefined,
120
+ dateFormat,
121
+ ),
122
+ meta: root ? { ...defaultConfig } : { ...config },
123
+ };
124
+
125
+ if (path.extname(file.name) === ".md") {
126
+ const markdownContent = await fs.readFile(filePath, "utf-8");
127
+ const { data, content } = matter(markdownContent);
128
+ Object.assign(page, { meta: { ...page.meta, ...data }, content });
129
+ page.title = page.meta.title || page.title;
130
+ page.name = page.meta.title || page.name;
131
+
132
+ // Allow front matter to override nav (opt in or opt out)
133
+ if (typeof data.nav === "boolean") {
134
+ page.nav = data.nav;
135
+ }
116
136
 
117
- if (path.extname(file.name) === ".md") {
118
- const markdownContent = await fs.readFile(filePath, "utf-8");
119
- const { data, content } = matter(markdownContent);
120
- Object.assign(page, { data: { ...page.data, ...data }, content });
121
-
122
- // If front matter has a date, parse and format it
123
- if (data.date) {
124
- const parsedDate = parseDate(data.date);
125
- if (parsedDate) {
126
- page.dateObj = parsedDate;
127
- page.date = parsedDate.toLocaleDateString(undefined, dateFormat);
128
- page.data.date = page.date;
137
+ // If front matter has a date, parse and format it
138
+ if (data.date) {
139
+ const parsedDate = parseDate(data.date);
140
+ if (parsedDate) {
141
+ page.dateObj = parsedDate;
142
+ page.date = parsedDate.toLocaleDateString(undefined, dateFormat);
143
+ page.meta.date = page.date;
144
+ }
129
145
  }
130
146
  }
131
- }
132
147
 
133
- // For directories, we defer recursion separately
134
- return { page, isDirectory };
135
- });
136
-
137
- // Await all file processing
138
- const fileResults = await Promise.all(filePromises);
148
+ // For directories, we defer recursion separately
149
+ return { page, isDirectory };
150
+ },
151
+ getBuildConcurrency(),
152
+ );
139
153
 
140
154
  // Now handle directories recursively
141
- const directoryPromises = fileResults.map(async (result) => {
155
+ await mapLimit(fileResults, async (result) => {
142
156
  if (!result) return;
143
157
 
144
158
  const { page, isDirectory } = result;
145
159
 
160
+ // Skip draft pages in production builds
161
+ if (page.meta.draft && !process.env.SWIFTY_WATCH) return;
162
+
163
+ // Skip scheduled pages (future date) in production builds
164
+ if (page.dateObj && page.dateObj > new Date() && !process.env.SWIFTY_WATCH) return;
165
+
146
166
  if (isDirectory) {
147
167
  page.pages = await generatePages(page.filePath, baseDir, page);
148
168
 
169
+ // Load folder's own config for pagination settings
170
+ const dirConfig = await loadConfig(page.filePath);
171
+ const mergedConfig = {
172
+ ...page.meta,
173
+ ...dirConfig,
174
+ // Only set default page_count if explicitly specified in either page meta or dir config
175
+ page_count: dirConfig.page_count || page.meta.page_count
176
+ };
177
+ page.meta = mergedConfig;
178
+
149
179
  page.pages.sort((a, b) => {
150
- if (a.data.position && b.data.position) {
151
- return a.data.position - b.data.position;
180
+ if (a.meta.position && b.meta.position) {
181
+ return a.meta.position - b.meta.position;
152
182
  }
153
183
  const dateA = a.dateObj || new Date(a.created_at);
154
184
  const dateB = b.dateObj || new Date(b.created_at);
155
- const sortOrder = (config.date_sort_order || "desc").toLowerCase();
185
+ const sortOrder = (mergedConfig.date_sort_order || "desc").toLowerCase();
156
186
  return sortOrder === "asc" ? dateA - dateB : dateB - dateA;
157
187
  });
158
188
 
159
- page.content = await generateLinkList(page.filename, page.pages);
189
+ // Handle pagination if page_count is set
190
+ const pageCount = mergedConfig.page_count;
191
+ if (pageCount && page.pages.length > pageCount) {
192
+ page.allPages = page.pages;
193
+ const chunks = chunkPages(page.pages, pageCount);
194
+ const totalPages = chunks.length;
195
+ const baseUrl = `${page.url}/`;
196
+
197
+ // Store visible pages for first page (used by links_to_children)
198
+ page.visiblePages = chunks[0];
199
+
200
+ // First page content
201
+ page.content = await generateLinkList(page.filename, chunks[0]);
202
+ page.meta.pagination = generatePaginationNav({
203
+ currentPage: 1,
204
+ totalPages,
205
+ baseUrl,
206
+ config: page.meta,
207
+ });
208
+
209
+ // Create pagination pages for page 2, 3, etc.
210
+ page.paginatedPages = [];
211
+ for (let i = 1; i < chunks.length; i++) {
212
+ const pageNum = i + 1;
213
+ const paginatedPage = {
214
+ name: `${page.name} - Page ${pageNum}`,
215
+ title: `${page.meta.title || page.name} - Page ${pageNum}`,
216
+ url: `${page.url}/page/${pageNum}`,
217
+ folder: false,
218
+ layout: page.layout,
219
+ parent: { title: page.meta.title || page.name, url: page.url },
220
+ meta: {
221
+ ...page.meta,
222
+ title: `${page.meta.title || page.name} - Page ${pageNum}`,
223
+ pagination: generatePaginationNav({
224
+ currentPage: pageNum,
225
+ totalPages,
226
+ baseUrl,
227
+ config: page.meta,
228
+ }),
229
+ },
230
+ content: await generateLinkList(page.filename, chunks[i]),
231
+ };
232
+ page.paginatedPages.push(paginatedPage);
233
+ }
234
+ } else {
235
+ page.content = await generateLinkList(page.filename, page.pages);
236
+ page.meta.pagination = '';
237
+ }
160
238
  }
161
239
 
162
240
  // Add tags
163
- if (page.data.tags) {
164
- page.data.tags.forEach((tag) => addToTagMap(tag, page));
241
+ if (page.meta.tags) {
242
+ page.meta.tags.forEach((tag) => addToTagMap(tag, page));
165
243
  }
166
244
 
167
245
  pages.push(page);
@@ -169,10 +247,7 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
169
247
  pageIndexUrls.add(page.url);
170
248
  pageIndex.push({ url: page.url, title: page.title, nav: page.nav });
171
249
  }
172
- });
173
-
174
- // Await all directory recursion
175
- await Promise.all(directoryPromises);
250
+ }, getBuildConcurrency());
176
251
  } catch (err) {
177
252
  console.error("Error reading directory:", err);
178
253
  }
@@ -191,7 +266,7 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
191
266
  undefined,
192
267
  defaultConfig.dateFormat,
193
268
  ),
194
- data: { ...config },
269
+ meta: { ...config },
195
270
  pages: [],
196
271
  };
197
272
 
@@ -205,7 +280,7 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
205
280
  ),
206
281
  url: `/tags/${tag}`,
207
282
  layout: tagLayout ? "tags" : defaultConfig.default_layout_name,
208
- data: { ...config, title: `Pages tagged with ${capitalize(tag)}` },
283
+ meta: { ...config, title: `Pages tagged with ${capitalize(tag)}` },
209
284
  };
210
285
  page.content = await generateLinkList("tags", pagesForTag);
211
286
 
@@ -218,24 +293,25 @@ const generatePages = async (sourceDir, baseDir = sourceDir, parent) => {
218
293
  };
219
294
 
220
295
  const generateLinkList = async (name, pages) => {
221
- const partialFile = `${name}.md`;
222
- const partialPath = path.join(dirs.partials, partialFile);
223
- const linksPath = path.join(
224
- dirs.partials,
296
+ const findPartialPath = async (partialName) => {
297
+ for (const ext of partialExtensions) {
298
+ const partialPath = path.join(dirs.partials, `${partialName}${ext}`);
299
+ if (await fsExtra.pathExists(partialPath)) return partialPath;
300
+ }
301
+ return null;
302
+ };
303
+
304
+ const partialPath = await findPartialPath(name);
305
+ const defaultPath = await findPartialPath(
225
306
  defaultConfig.default_link_name || "links",
226
307
  );
227
- // Check if either file exists in the 'partials' folder (in parallel)
228
- const [fileExists, defaultExists] = await Promise.all([
229
- fsExtra.pathExists(partialPath),
230
- fsExtra.pathExists(linksPath),
231
- ]);
232
- if (fileExists || defaultExists) {
233
- const partialContent = await fs.readFile(
234
- fileExists ? partialPath : linksPath,
235
- "utf-8",
236
- );
237
- const content = await Promise.all(
238
- pages.map((page) => replacePlaceholders(partialContent, page)),
308
+
309
+ if (partialPath || defaultPath) {
310
+ const partialContent = await fs.readFile(partialPath || defaultPath, "utf-8");
311
+ const content = await mapLimit(
312
+ pages,
313
+ (page) => replacePlaceholders(partialContent, page),
314
+ getBuildConcurrency(),
239
315
  );
240
316
  return content.join("\n");
241
317
  } else {
@@ -255,7 +331,7 @@ const render = async (page) => {
255
331
  // Use function to avoid $` special replacement patterns in content
256
332
  const template = await getTemplate();
257
333
  const htmlWithTemplate = template.replace(
258
- /\{\{\s*content\s*\}\}/g,
334
+ /<%=\s*content\s*%>/g,
259
335
  () => wrappedContent,
260
336
  );
261
337
  const finalContent = await replacePlaceholders(htmlWithTemplate, page);
@@ -263,45 +339,78 @@ const render = async (page) => {
263
339
  };
264
340
 
265
341
  const createPages = async (pages, distDir = dirs.dist) => {
266
- await Promise.all(
267
- pages.map(async (page) => {
342
+ await mapLimit(
343
+ pages,
344
+ async (page) => {
268
345
  const html = await render(page);
269
346
  const pageDir = path.join(distDir, page.url);
270
347
  const pagePath = path.join(distDir, page.url, "index.html");
271
348
  await fsExtra.ensureDir(pageDir);
272
349
  await fs.writeFile(pagePath, html);
273
350
  if (page.folder) {
274
- await createPages(page.pages, distDir); // Recursive still needs to await
351
+ await createPages(page.pages, distDir);
352
+ // Write pagination pages if they exist
353
+ if (page.paginatedPages?.length) {
354
+ await createPages(page.paginatedPages, distDir);
355
+ }
275
356
  }
276
- }),
357
+ },
358
+ getBuildConcurrency(),
277
359
  );
278
360
  };
279
361
 
280
362
  const addLinks = async (pages, parent) => {
281
- await Promise.all(
282
- pages.map(async (page) => {
283
- page.data ||= {};
284
- page.data.links_to_tags = page?.data?.tags?.length
285
- ? page.data.tags
363
+ // Filter out folders and index pages for prev/next calculation (only content pages)
364
+ const contentPages = pages.filter((p) => !p.folder && p.filename !== 'index');
365
+
366
+ await mapLimit(
367
+ pages,
368
+ async (page) => {
369
+ page.meta ||= {};
370
+ page.meta.links_to_tags = page?.meta?.tags?.length
371
+ ? page.meta.tags
286
372
  .map(
287
373
  (tag) =>
288
374
  `<a class="${defaultConfig.tag_class}" href="/tags/${tag}">${tag}</a>`,
289
375
  )
290
376
  .join("")
291
377
  : "";
378
+ const crumbTitle = page.meta.title || page.title || page.name;
292
379
  const crumb = page.root
293
380
  ? ""
294
- : ` ${defaultConfig.breadcrumb_separator} <a class="${defaultConfig.breadcrumb_class}" href="${page.url}">${page.name}</a>`;
295
- page.data.breadcrumbs = parent
296
- ? parent.data.breadcrumbs + crumb
381
+ : ` ${defaultConfig.breadcrumb_separator} <a class="${defaultConfig.breadcrumb_class}" href="${page.url}">${crumbTitle}</a>`;
382
+ page.meta.breadcrumbs = parent
383
+ ? parent.meta.breadcrumbs + crumb
297
384
  : `<a class="${defaultConfig.breadcrumb_class}" href="/">Home</a>` +
298
385
  crumb;
299
386
 
387
+ // Generate prev/next links based on sibling position (only for content pages, not folders or index)
388
+ const linkClass = defaultConfig.prev_next_class || defaultConfig.link_class || '';
389
+ const classAttr = linkClass ? ` class="${linkClass}"` : '';
390
+
391
+ if (!page.folder && page.filename !== 'index') {
392
+ const pageIndex = contentPages.indexOf(page);
393
+ const prevSibling = pageIndex > 0 ? contentPages[pageIndex - 1] : null;
394
+ const nextSibling = pageIndex < contentPages.length - 1 ? contentPages[pageIndex + 1] : null;
395
+
396
+ page.meta.prev_page = prevSibling
397
+ ? `<a href="${prevSibling.url}"${classAttr}>${prevSibling.title || prevSibling.name}</a>`
398
+ : '';
399
+ page.meta.next_page = nextSibling
400
+ ? `<a href="${nextSibling.url}"${classAttr}>${nextSibling.title || nextSibling.name}</a>`
401
+ : '';
402
+ } else {
403
+ page.meta.prev_page = '';
404
+ page.meta.next_page = '';
405
+ }
406
+
300
407
  // Run independent link generation in parallel
408
+ // Use visiblePages for paginated folders (first page only shows its chunk)
409
+ const childPages = page.visiblePages || page.pages;
301
410
  const [links_to_children, links_to_siblings, links_to_self_and_siblings, nav_links] =
302
411
  await Promise.all([
303
- page.pages
304
- ? generateLinkList(page.filename, page.pages)
412
+ childPages
413
+ ? generateLinkList(page.filename, childPages)
305
414
  : Promise.resolve(""),
306
415
  generateLinkList(
307
416
  page.parent?.filename || "pages",
@@ -311,15 +420,24 @@ const addLinks = async (pages, parent) => {
311
420
  generateLinkList("nav", pageIndex.filter((p) => p.nav)),
312
421
  ]);
313
422
 
314
- page.data.links_to_children = links_to_children;
315
- page.data.links_to_siblings = links_to_siblings;
316
- page.data.links_to_self_and_siblings = links_to_self_and_siblings;
317
- page.data.nav_links = nav_links;
423
+ page.meta.links_to_children = links_to_children;
424
+ page.meta.links_to_siblings = links_to_siblings;
425
+ page.meta.links_to_self_and_siblings = links_to_self_and_siblings;
426
+ page.meta.nav_links = nav_links;
318
427
 
319
428
  if (page.pages) {
320
429
  await addLinks(page.pages, page);
321
430
  }
322
- }),
431
+
432
+ // Process pagination pages - inherit breadcrumbs and nav_links
433
+ if (page.paginatedPages) {
434
+ for (const pp of page.paginatedPages) {
435
+ pp.meta.breadcrumbs = page.meta.breadcrumbs;
436
+ pp.meta.nav_links = nav_links;
437
+ }
438
+ }
439
+ },
440
+ getBuildConcurrency(),
323
441
  );
324
442
  };
325
443