@stati/core 1.28.0 → 1.29.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.
Files changed (63) hide show
  1. package/README.md +6 -1
  2. package/dist/core/build.d.ts.map +1 -1
  3. package/dist/core/build.js +122 -34
  4. package/dist/core/collections.d.ts +139 -0
  5. package/dist/core/collections.d.ts.map +1 -0
  6. package/dist/core/collections.js +342 -0
  7. package/dist/core/content.d.ts.map +1 -1
  8. package/dist/core/content.js +2 -1
  9. package/dist/core/dev.d.ts.map +1 -1
  10. package/dist/core/dev.js +11 -62
  11. package/dist/core/expand.d.ts +86 -0
  12. package/dist/core/expand.d.ts.map +1 -0
  13. package/dist/core/expand.js +484 -0
  14. package/dist/core/isg/builder.d.ts +15 -2
  15. package/dist/core/isg/builder.d.ts.map +1 -1
  16. package/dist/core/isg/builder.js +44 -6
  17. package/dist/core/isg/hash.d.ts +4 -2
  18. package/dist/core/isg/hash.d.ts.map +1 -1
  19. package/dist/core/isg/hash.js +16 -2
  20. package/dist/core/isg/index.d.ts +2 -1
  21. package/dist/core/isg/index.d.ts.map +1 -1
  22. package/dist/core/isg/index.js +3 -1
  23. package/dist/core/isg/manifest.js +6 -0
  24. package/dist/core/isg/prune.d.ts +32 -0
  25. package/dist/core/isg/prune.d.ts.map +1 -0
  26. package/dist/core/isg/prune.js +113 -0
  27. package/dist/core/navigation.d.ts +4 -2
  28. package/dist/core/navigation.d.ts.map +1 -1
  29. package/dist/core/navigation.js +17 -2
  30. package/dist/core/templates.d.ts +3 -0
  31. package/dist/core/templates.d.ts.map +1 -1
  32. package/dist/core/templates.js +67 -127
  33. package/dist/core/utils/fs.utils.d.ts +8 -0
  34. package/dist/core/utils/fs.utils.d.ts.map +1 -1
  35. package/dist/core/utils/fs.utils.js +13 -1
  36. package/dist/core/utils/index.d.ts +3 -3
  37. package/dist/core/utils/index.d.ts.map +1 -1
  38. package/dist/core/utils/index.js +3 -3
  39. package/dist/core/utils/paths.utils.d.ts +15 -0
  40. package/dist/core/utils/paths.utils.d.ts.map +1 -1
  41. package/dist/core/utils/paths.utils.js +20 -0
  42. package/dist/core/utils/template-discovery.utils.d.ts +25 -1
  43. package/dist/core/utils/template-discovery.utils.d.ts.map +1 -1
  44. package/dist/core/utils/template-discovery.utils.js +61 -11
  45. package/dist/index.d.ts +1 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/rss/generator.d.ts.map +1 -1
  48. package/dist/rss/generator.js +5 -2
  49. package/dist/search/generator.d.ts +9 -0
  50. package/dist/search/generator.d.ts.map +1 -1
  51. package/dist/search/generator.js +20 -0
  52. package/dist/search/index.d.ts +1 -1
  53. package/dist/search/index.d.ts.map +1 -1
  54. package/dist/search/index.js +1 -1
  55. package/dist/types/config.d.ts +55 -0
  56. package/dist/types/config.d.ts.map +1 -1
  57. package/dist/types/content.d.ts +129 -1
  58. package/dist/types/content.d.ts.map +1 -1
  59. package/dist/types/index.d.ts +2 -2
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/types/isg.d.ts +12 -0
  62. package/dist/types/isg.d.ts.map +1 -1
  63. package/package.json +1 -1
@@ -131,4 +131,19 @@ export declare function normalizePathForComparison(filePath: string, basePath?:
131
131
  * ```
132
132
  */
133
133
  export declare function isPathWithinDirectory(baseDir: string, targetPath: string): boolean;
134
+ /**
135
+ * The file a page URL is written to, relative to the output directory. The build joins it to
136
+ * the output directory, which also normalizes separators and `.` segments.
137
+ *
138
+ * @param url - The page URL
139
+ * @returns The output file
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * getPageOutputFile('/'); // 'index.html'
144
+ * getPageOutputFile('/blog/'); // '/blog/index.html'
145
+ * getPageOutputFile('/blog/post'); // '/blog/post.html'
146
+ * ```
147
+ */
148
+ export declare function getPageOutputFile(url: string): string;
134
149
  //# sourceMappingURL=paths.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/paths.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQxD;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW;;;;EAMlD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BtF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAYlF"}
1
+ {"version":3,"file":"paths.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/paths.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQxD;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW;;;;EAMlD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BtF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAYlF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKrD"}
@@ -183,3 +183,23 @@ export function isPathWithinDirectory(baseDir, targetPath) {
183
183
  // We also allow exact match (resolvedTarget === resolvedBase) for the root
184
184
  return resolvedTarget === resolvedBase || resolvedTarget.startsWith(normalizedBase);
185
185
  }
186
+ /**
187
+ * The file a page URL is written to, relative to the output directory. The build joins it to
188
+ * the output directory, which also normalizes separators and `.` segments.
189
+ *
190
+ * @param url - The page URL
191
+ * @returns The output file
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * getPageOutputFile('/'); // 'index.html'
196
+ * getPageOutputFile('/blog/'); // '/blog/index.html'
197
+ * getPageOutputFile('/blog/post'); // '/blog/post.html'
198
+ * ```
199
+ */
200
+ export function getPageOutputFile(url) {
201
+ if (url === '/') {
202
+ return 'index.html';
203
+ }
204
+ return url.endsWith('/') ? `${url}index.html` : `${url}.html`;
205
+ }
@@ -1,13 +1,37 @@
1
1
  import type { StatiConfig, PageModel } from '../../types/index.js';
2
+ /**
3
+ * Shared template discovery utilities.
4
+ * Extracted from templates.ts and isg/deps.ts to eliminate duplication.
5
+ */
6
+ /**
7
+ * Whether a page was created by Stati rather than loaded from a Markdown file: a page expanded
8
+ * from a `[param].md` template, or page 2 or later of a paginated listing.
9
+ *
10
+ * Virtual pages are never collection members, never make a folder a collection, and are left
11
+ * out of navigation, search and RSS unless they opt in.
12
+ *
13
+ * @param page - The page to check
14
+ * @returns True for generated pages and for pagination pages after the first
15
+ */
16
+ export declare function isVirtualPage(page: PageModel): boolean;
17
+ /**
18
+ * Finds the first page of a paginated listing, which later pages render like.
19
+ *
20
+ * @param page - A page with `pagination`
21
+ * @param allPages - All pages in the site
22
+ * @returns The listing's first page, or undefined when it is not in the list
23
+ */
24
+ export declare function getFirstPaginationPage(page: PageModel, allPages: PageModel[]): PageModel | undefined;
2
25
  /**
3
26
  * Determines if the given page is a collection index page.
4
27
  * A collection index page is one whose URL matches a directory path that contains other pages.
28
+ * Generated pages never are; pagination pages after the first are when their first page is.
5
29
  *
6
30
  * Rendering and ISG dependency tracking must both call this with the same page list,
7
31
  * otherwise they can pick different layouts for the same page.
8
32
  *
9
33
  * Page URLs are read once per `allPages` array, at the first call; see
10
- * `directoryPrefixCache` for when changes to the pages are picked up.
34
+ * `pageListCache` for when changes to the pages are picked up.
11
35
  *
12
36
  * @param page - The page to check
13
37
  * @param allPages - All pages in the site (treated as unchanging after the first call)
@@ -1 +1 @@
1
- {"version":3,"file":"template-discovery.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/template-discovery.utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAoDnE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAUrF;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,cAAc,CAAC,EAAE,MAAM,EACvB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsDxB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBhE"}
1
+ {"version":3,"file":"template-discovery.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/template-discovery.utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAInE;;;GAGG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAEtD;AAiED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,SAAS,EAAE,GACpB,SAAS,GAAG,SAAS,CAMvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAoBrF;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,cAAc,CAAC,EAAE,MAAM,EACvB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsDxB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBhE"}
@@ -2,6 +2,23 @@ import { join, dirname, posix } from 'node:path';
2
2
  import { pathExists } from './fs.utils.js';
3
3
  import { LAYOUT_TEMPLATE } from '../../constants.js';
4
4
  import { resolveSrcDir } from './paths.utils.js';
5
+ /**
6
+ * Shared template discovery utilities.
7
+ * Extracted from templates.ts and isg/deps.ts to eliminate duplication.
8
+ */
9
+ /**
10
+ * Whether a page was created by Stati rather than loaded from a Markdown file: a page expanded
11
+ * from a `[param].md` template, or page 2 or later of a paginated listing.
12
+ *
13
+ * Virtual pages are never collection members, never make a folder a collection, and are left
14
+ * out of navigation, search and RSS unless they opt in.
15
+ *
16
+ * @param page - The page to check
17
+ * @returns True for generated pages and for pagination pages after the first
18
+ */
19
+ export function isVirtualPage(page) {
20
+ return page.generated !== undefined || (page.pagination?.pageNumber ?? 1) > 1;
21
+ }
5
22
  /**
6
23
  * Directory prefix index per page list, so a build computes it once instead of
7
24
  * scanning every page for every page.
@@ -13,51 +30,84 @@ import { resolveSrcDir } from './paths.utils.js';
13
30
  * loads a new array and gets a new snapshot. Taking one snapshot keeps rendering and
14
31
  * ISG dependency tracking in agreement for the whole build.
15
32
  */
16
- const directoryPrefixCache = new WeakMap();
17
- function getDirectoryPrefixIndex(allPages) {
18
- const cached = directoryPrefixCache.get(allPages);
33
+ const pageListCache = new WeakMap();
34
+ function getPageListIndex(allPages) {
35
+ const cached = pageListCache.get(allPages);
19
36
  if (cached) {
20
37
  return cached;
21
38
  }
22
- const index = new Map();
23
- for (const { url } of allPages) {
39
+ const prefixes = new Map();
40
+ const byUrl = new Map();
41
+ for (const page of allPages) {
42
+ if (!byUrl.has(page.url)) {
43
+ byUrl.set(page.url, page);
44
+ }
45
+ // Generated and pagination pages don't turn the folders they live in into collections
46
+ if (isVirtualPage(page)) {
47
+ continue;
48
+ }
49
+ const { url } = page;
24
50
  // Every prefix ending at a '/' is a directory this URL lives in (or is, for trailing-slash URLs)
25
51
  for (let i = url.indexOf('/'); i !== -1; i = url.indexOf('/', i + 1)) {
26
52
  const prefix = url.slice(0, i + 1);
27
- const existing = index.get(prefix);
53
+ const existing = prefixes.get(prefix);
28
54
  if (existing === undefined) {
29
- index.set(prefix, url);
55
+ prefixes.set(prefix, url);
30
56
  }
31
57
  else if (existing !== null && existing !== url) {
32
- index.set(prefix, null);
58
+ prefixes.set(prefix, null);
33
59
  }
34
60
  }
35
61
  }
36
- directoryPrefixCache.set(allPages, index);
62
+ const index = { prefixes, byUrl };
63
+ pageListCache.set(allPages, index);
37
64
  return index;
38
65
  }
66
+ /**
67
+ * Finds the first page of a paginated listing, which later pages render like.
68
+ *
69
+ * @param page - A page with `pagination`
70
+ * @param allPages - All pages in the site
71
+ * @returns The listing's first page, or undefined when it is not in the list
72
+ */
73
+ export function getFirstPaginationPage(page, allPages) {
74
+ const firstUrl = page.pagination?.urls[0];
75
+ if (firstUrl === undefined || firstUrl === page.url) {
76
+ return undefined;
77
+ }
78
+ return getPageListIndex(allPages).byUrl.get(firstUrl);
79
+ }
39
80
  /**
40
81
  * Determines if the given page is a collection index page.
41
82
  * A collection index page is one whose URL matches a directory path that contains other pages.
83
+ * Generated pages never are; pagination pages after the first are when their first page is.
42
84
  *
43
85
  * Rendering and ISG dependency tracking must both call this with the same page list,
44
86
  * otherwise they can pick different layouts for the same page.
45
87
  *
46
88
  * Page URLs are read once per `allPages` array, at the first call; see
47
- * `directoryPrefixCache` for when changes to the pages are picked up.
89
+ * `pageListCache` for when changes to the pages are picked up.
48
90
  *
49
91
  * @param page - The page to check
50
92
  * @param allPages - All pages in the site (treated as unchanging after the first call)
51
93
  * @returns True if the page is a collection index page
52
94
  */
53
95
  export function isCollectionIndexPage(page, allPages) {
96
+ if (page.generated) {
97
+ return false;
98
+ }
99
+ // Later pages of a listing render with the layout of its first page
100
+ if ((page.pagination?.pageNumber ?? 1) > 1) {
101
+ const first = getFirstPaginationPage(page, allPages);
102
+ return first !== undefined && !isVirtualPage(first) && isCollectionIndexPage(first, allPages);
103
+ }
54
104
  // Root index page is always a collection index
55
105
  if (page.url === '/') {
56
106
  return true;
57
107
  }
58
108
  // The page is an index when a different page's URL lives under this page's URL as a directory
59
109
  const pageUrlAsDir = page.url.endsWith('/') ? page.url : page.url + '/';
60
- const containingUrl = getDirectoryPrefixIndex(allPages).get(pageUrlAsDir);
110
+ const containingUrl = getPageListIndex(allPages).prefixes.get(pageUrlAsDir);
61
111
  return containingUrl === null || (containingUrl !== undefined && containingUrl !== page.url);
62
112
  }
63
113
  /**
package/dist/index.d.ts CHANGED
@@ -19,7 +19,7 @@
19
19
  * await build({ clean: true });
20
20
  * ```
21
21
  */
22
- export type { SiteConfig, StatiConfig, PageModel, FrontMatter, BuildContext, PageContext, BuildHooks, NavNode, ISGConfig, AgingRule, BuildStats, BundleConfig, } from './types/index.js';
22
+ export type { SiteConfig, StatiConfig, PageModel, FrontMatter, BuildContext, PageContext, BuildHooks, NavNode, ISGConfig, AgingRule, BuildStats, BundleConfig, CollectionData, CollectionsConfig, GeneratedPageEntry, PaginationData, GeneratedPageData, PaginateOptions, GenerateOptions, } from './types/index.js';
23
23
  export type { SEOMetadata, SEOConfig, SEOContext, SEOValidationResult, SEOTagType, RobotsConfig, OpenGraphConfig, OpenGraphImage, OpenGraphArticle, TwitterCardConfig, AuthorConfig, } from './types/index.js';
24
24
  export type { SitemapConfig, SitemapEntry, SitemapGenerationResult, ChangeFrequency, } from './types/index.js';
25
25
  export type { RSSConfig, RSSFeedConfig, RSSGenerationResult } from './types/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,YAAY,EACV,UAAU,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,WAAW,EACX,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGtF,YAAY,EACV,YAAY,EACZ,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,aAAa,EACb,cAAc,EACd,MAAM,EACN,UAAU,EACV,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1F,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAG5F,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAG/E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,YAAY,EACV,UAAU,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,WAAW,EACX,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGtF,YAAY,EACV,YAAY,EACZ,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,aAAa,EACb,cAAc,EACd,MAAM,EACN,UAAU,EACV,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1F,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACT,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAG5F,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAG/E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/rss/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA4KlD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,aAAa,EACzB,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,CA4LrB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,WAAW,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,EAAE,CAcvB"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/rss/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA+KlD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,aAAa,EACzB,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,CA4LrB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,WAAW,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,EAAE,CAcvB"}
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { escapeHtml } from '../seo/utils/index.js';
6
6
  import { filterByPatterns } from './utils/index.js';
7
- import { getFrontMatterDate } from '../core/utils/index.js';
7
+ import { getFrontMatterDate, isVirtualPage } from '../core/utils/index.js';
8
8
  /**
9
9
  * Formats a date for RSS pubDate field (RFC 822)
10
10
  * @param date - Date to format
@@ -47,7 +47,10 @@ function filterPages(pages, feedConfig) {
47
47
  if (feedConfig.excludePatterns) {
48
48
  options.excludePatterns = feedConfig.excludePatterns;
49
49
  }
50
- let filtered = filterByPatterns(pages, (page) => page.sourcePath, options);
50
+ // Generated and pagination pages share their template's source path, so patterns can't
51
+ // tell them apart from content; feeds list content only
52
+ const contentPages = pages.filter((page) => !isVirtualPage(page));
53
+ let filtered = filterByPatterns(contentPages, (page) => page.sourcePath, options);
51
54
  // Apply custom filter function if provided
52
55
  if (feedConfig.filter) {
53
56
  filtered = filtered.filter(feedConfig.filter);
@@ -50,6 +50,15 @@ export declare function extractSectionsFromMarkdown(toc: TocEntry[], markdownCon
50
50
  * @returns true if the page should be excluded
51
51
  */
52
52
  export declare function shouldExcludePage(page: PageModel, config: SearchConfig): boolean;
53
+ /**
54
+ * Whether a page created by Stati stays out of the search index. Pages generated from a
55
+ * `[param].md` template are left out unless the template sets `search: true`; later pages
56
+ * of a paginated listing are always left out, since they repeat the first page's content.
57
+ *
58
+ * @param page - Page model to check
59
+ * @returns true for generated and pagination pages that are not indexed
60
+ */
61
+ export declare function isGeneratedPageExcludedFromSearch(page: PageModel): boolean;
53
62
  /**
54
63
  * Page data for search indexing using markdown and TOC entries.
55
64
  * Uses structured data already available from the markdown processing pipeline.
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/search/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAqBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CA2BtE;AAKD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8DtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,QAAQ,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,MAAM,EAAE,YAAY,GACnB,cAAc,EAAE,CAiGlB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAsBhF;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,oEAAoE;IACpE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,kDAAkD;IAClD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,cAAc,EAAE,EACjC,MAAM,EAAE,YAAY,GACnB,WAAW,CA6Bb;AAiBD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,UAAQ,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAqC9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAczF"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/search/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAqBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CA2BtE;AAKD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8DtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,QAAQ,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,MAAM,EAAE,YAAY,GACnB,cAAc,EAAE,CAiGlB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CA2BhF;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAM1E;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,oEAAoE;IACpE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,kDAAkD;IAClD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,cAAc,EAAE,EACjC,MAAM,EAAE,YAAY,GACnB,WAAW,CA6Bb;AAiBD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,UAAQ,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAqC9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAczF"}
@@ -6,6 +6,7 @@ import { createHash } from 'node:crypto';
6
6
  import { join } from 'node:path';
7
7
  import { writeFile, ensureDir } from '../core/utils/fs.utils.js';
8
8
  import { matchesGlob } from '../core/utils/glob-patterns.utils.js';
9
+ import { isVirtualPage } from '../core/utils/template-discovery.utils.js';
9
10
  import { SEARCH_INDEX_VERSION, SEARCH_DEFAULTS } from './constants.js';
10
11
  /**
11
12
  * Generates a short content hash for cache busting.
@@ -218,6 +219,10 @@ export function shouldExcludePage(page, config) {
218
219
  if (page.frontMatter.draft) {
219
220
  return true;
220
221
  }
222
+ // Exclude generated and pagination pages unless their template opts in
223
+ if (isGeneratedPageExcludedFromSearch(page)) {
224
+ return true;
225
+ }
221
226
  // Check home page exclusion
222
227
  const isHomePage = page.url === '/' || page.url === '';
223
228
  const includeHomePage = config.includeHomePage ?? SEARCH_DEFAULTS.includeHomePage;
@@ -233,6 +238,21 @@ export function shouldExcludePage(page, config) {
233
238
  }
234
239
  return false;
235
240
  }
241
+ /**
242
+ * Whether a page created by Stati stays out of the search index. Pages generated from a
243
+ * `[param].md` template are left out unless the template sets `search: true`; later pages
244
+ * of a paginated listing are always left out, since they repeat the first page's content.
245
+ *
246
+ * @param page - Page model to check
247
+ * @returns true for generated and pagination pages that are not indexed
248
+ */
249
+ export function isGeneratedPageExcludedFromSearch(page) {
250
+ if (!isVirtualPage(page)) {
251
+ return false;
252
+ }
253
+ const firstPage = (page.pagination?.pageNumber ?? 1) === 1;
254
+ return !(page.generated && firstPage && page.frontMatter.search === true);
255
+ }
236
256
  /**
237
257
  * Generates a search index from searchable pages using markdown and TOC data.
238
258
  *
@@ -16,7 +16,7 @@
16
16
  * console.log(`Generated search index at ${metadata.indexPath}`);
17
17
  * ```
18
18
  */
19
- export { generateSearchIndex, writeSearchIndex, extractSectionsFromMarkdown, stripMarkdown, shouldExcludePage, generateContentHash, buildBreadcrumb, computeSearchIndexFilename, } from './generator.js';
19
+ export { generateSearchIndex, writeSearchIndex, extractSectionsFromMarkdown, stripMarkdown, shouldExcludePage, isGeneratedPageExcludedFromSearch, generateContentHash, buildBreadcrumb, computeSearchIndexFilename, } from './generator.js';
20
20
  export type { SearchablePage } from './generator.js';
21
21
  export { autoInjectSearchMeta } from './auto-inject.js';
22
22
  export { SEARCH_INDEX_META_NAME, SEARCH_INDEX_VERSION, SEARCH_DEFAULTS } from './constants.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE/F,YAAY,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,GACpB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,EACb,iBAAiB,EACjB,iCAAiC,EACjC,mBAAmB,EACnB,eAAe,EACf,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE/F,YAAY,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,GACpB,MAAM,oBAAoB,CAAC"}
@@ -16,7 +16,7 @@
16
16
  * console.log(`Generated search index at ${metadata.indexPath}`);
17
17
  * ```
18
18
  */
19
- export { generateSearchIndex, writeSearchIndex, extractSectionsFromMarkdown, stripMarkdown, shouldExcludePage, generateContentHash, buildBreadcrumb, computeSearchIndexFilename, } from './generator.js';
19
+ export { generateSearchIndex, writeSearchIndex, extractSectionsFromMarkdown, stripMarkdown, shouldExcludePage, isGeneratedPageExcludedFromSearch, generateContentHash, buildBreadcrumb, computeSearchIndexFilename, } from './generator.js';
20
20
  // Auto-injection
21
21
  export { autoInjectSearchMeta } from './auto-inject.js';
22
22
  export { SEARCH_INDEX_META_NAME, SEARCH_INDEX_VERSION, SEARCH_DEFAULTS } from './constants.js';
@@ -73,6 +73,8 @@ export interface StatiConfig {
73
73
  };
74
74
  /** Incremental Static Generation configuration */
75
75
  isg?: import('./isg.js').ISGConfig;
76
+ /** Collection listings: which fields rebuild them, and pages generated from data */
77
+ collections?: CollectionsConfig;
76
78
  /** SEO configuration */
77
79
  seo?: SEOConfig;
78
80
  /** Sitemap generation configuration */
@@ -111,6 +113,59 @@ export interface StatiConfig {
111
113
  /** Build lifecycle hooks */
112
114
  hooks?: BuildHooks;
113
115
  }
116
+ /**
117
+ * Collection settings.
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * const collections: CollectionsConfig = {
122
+ * // Listings also show each post's image, so changing it rebuilds them
123
+ * summaryFields: ['title', 'description', 'tags', 'draft', 'image'],
124
+ * // One page per author, rendered with site/authors/[author].md
125
+ * generate: ({ pages }) => [
126
+ * {
127
+ * url: '/authors/ada',
128
+ * template: 'authors/[author].md',
129
+ * label: 'Ada Lovelace',
130
+ * items: pages.filter((page) => page.frontMatter.author === 'ada'),
131
+ * },
132
+ * ],
133
+ * };
134
+ * ```
135
+ */
136
+ export interface CollectionsConfig {
137
+ /**
138
+ * Front matter fields of collection pages that listings show. A page that reads
139
+ * `stati.collection` rebuilds when one of these fields changes on a page in the collection,
140
+ * and so do paginated and generated pages that list it. Each page's URL and publish date
141
+ * always count. `content` stands for the Markdown body, for listings that show excerpts.
142
+ *
143
+ * @default ['title', 'description', 'tags', 'draft']
144
+ */
145
+ summaryFields?: string[];
146
+ /**
147
+ * Pages to create from data, each rendered with a `[param].md` template. Runs once per build,
148
+ * after content is loaded and before navigation is built.
149
+ */
150
+ generate?: (ctx: BuildContext) => GeneratedPageEntry[] | Promise<GeneratedPageEntry[]>;
151
+ }
152
+ /**
153
+ * A page returned by `collections.generate`.
154
+ */
155
+ export interface GeneratedPageEntry {
156
+ /** URL of the page, such as `/authors/ada` */
157
+ url: string;
158
+ /** The `[param].md` template that renders it, relative to the source directory */
159
+ template: string;
160
+ /** `stati.generated.key`; defaults to the last segment of `url` */
161
+ key?: string;
162
+ /** `stati.generated.label` and the value of `{param}` placeholders; defaults to `key` */
163
+ label?: string;
164
+ /** Pages the page lists (`stati.generated.items`), split up by the template's `generate.paginate` */
165
+ items?: import('./content.js').PageModel[];
166
+ /** Any JSON-serializable data, as `stati.generated.data`; changing it rebuilds the page */
167
+ data?: Record<string, unknown>;
168
+ }
114
169
  /**
115
170
  * SEO configuration for the site.
116
171
  * Controls automatic SEO metadata injection and site-wide defaults.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"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"}
@@ -98,9 +98,62 @@ export interface FrontMatter {
98
98
  seo?: SEOMetadata;
99
99
  /** Sitemap configuration for the page */
100
100
  sitemap?: SitemapMetadata;
101
+ /**
102
+ * URL of the collection index page this page reads as `stati.collection`, such as `/posts`.
103
+ * Without it, an index page reads its own collection and other pages the nearest collection
104
+ * they belong to.
105
+ */
106
+ collection?: string;
107
+ /**
108
+ * Splits the page's collection (`stati.collection.recentPages`) into pages of this size.
109
+ * Page 1 keeps the page's URL; later pages get `<url>/page/2` and so on.
110
+ */
111
+ paginate?: number | PaginateOptions;
112
+ /** Expansion of a `[param].md` template into one page per key */
113
+ generate?: GenerateOptions;
114
+ /** Includes the pages a `[param].md` template generates in the navigation tree */
115
+ nav?: boolean;
116
+ /** Includes the pages a `[param].md` template generates in the search index */
117
+ search?: boolean;
101
118
  /** Additional custom properties */
102
119
  [key: string]: unknown;
103
120
  }
121
+ /**
122
+ * Pagination settings from front matter (`paginate`).
123
+ *
124
+ * @example
125
+ * ```yaml
126
+ * paginate:
127
+ * size: 10 # items per page
128
+ * path: page # optional, default 'page' → /blog/page/2
129
+ * ```
130
+ */
131
+ export interface PaginateOptions {
132
+ /** Items per page */
133
+ size: number;
134
+ /** URL segment before the page number (default `'page'`) */
135
+ path?: string;
136
+ }
137
+ /**
138
+ * Settings of a `[param].md` template (`generate` in its front matter).
139
+ *
140
+ * @example
141
+ * ```yaml
142
+ * # site/blog/tags/[tag].md
143
+ * generate:
144
+ * from: tags # one page per tag in the collection
145
+ * paginate: { size: 10 } # optional
146
+ * ```
147
+ */
148
+ export interface GenerateOptions {
149
+ /**
150
+ * Front matter field whose values become the keys, read from the pages of the template's
151
+ * collection. Works with string fields (`author`) and lists of strings (`tags`).
152
+ */
153
+ from?: string;
154
+ /** Splits each generated page's items into pages of this size */
155
+ paginate?: number | PaginateOptions;
156
+ }
104
157
  /**
105
158
  * SEO metadata configuration for a page.
106
159
  * Provides comprehensive control over meta tags, Open Graph, Twitter Cards, and structured data.
@@ -302,6 +355,72 @@ export interface PageModel {
302
355
  content: string;
303
356
  /** Publication date, from the first valid of `publishedAt`, `published`, `date`, `createdAt` */
304
357
  publishedAt?: Date;
358
+ /**
359
+ * Position of the page in a paginated listing. Set on every page of a listing with
360
+ * `paginate`, including the first one. Templates read it as `stati.pagination`.
361
+ */
362
+ pagination?: PaginationData;
363
+ /**
364
+ * Set on pages expanded from a `[param].md` template or a `collections.generate` entry.
365
+ * Templates read it as `stati.generated`.
366
+ */
367
+ generated?: GeneratedPageData;
368
+ }
369
+ /**
370
+ * One page of a paginated listing, available to templates as `stati.pagination`.
371
+ *
372
+ * @example
373
+ * ```typescript
374
+ * const pagination: PaginationData = {
375
+ * pageNumber: 2,
376
+ * totalPages: 7,
377
+ * items: [], // this page's slice of the listing
378
+ * prevUrl: '/blog',
379
+ * nextUrl: '/blog/page/3',
380
+ * urls: ['/blog', '/blog/page/2', '/blog/page/3'],
381
+ * };
382
+ * ```
383
+ */
384
+ export interface PaginationData {
385
+ /** Number of this page, starting at 1 */
386
+ pageNumber: number;
387
+ /** Number of pages in the listing; at least 1, even when there are no items */
388
+ totalPages: number;
389
+ /** The pages listed on this page: a slice of `recentPages`, or of `stati.generated.items` */
390
+ items: PageModel[];
391
+ /** URL of the previous page; absent on the first page */
392
+ prevUrl?: string;
393
+ /** URL of the next page; absent on the last page */
394
+ nextUrl?: string;
395
+ /** URL of every page of the listing, in order; `urls[0]` is the first page */
396
+ urls: string[];
397
+ }
398
+ /**
399
+ * Data of a page expanded from a `[param].md` template, available to templates as
400
+ * `stati.generated`.
401
+ *
402
+ * @example
403
+ * ```typescript
404
+ * // site/blog/tags/[tag].md, for the tag "CSS"
405
+ * const generated: GeneratedPageData = {
406
+ * key: 'css',
407
+ * label: 'CSS',
408
+ * items: [], // pages tagged "CSS", newest first
409
+ * template: 'blog/tags/[tag].md',
410
+ * };
411
+ * ```
412
+ */
413
+ export interface GeneratedPageData {
414
+ /** The key in the page URL, slugified for keys taken from front matter */
415
+ key: string;
416
+ /** The original value, used for `{param}` placeholders in front matter */
417
+ label: string;
418
+ /** The pages for this key, newest first */
419
+ items: PageModel[];
420
+ /** The template, relative to the source directory, such as `blog/tags/[tag].md` */
421
+ template: string;
422
+ /** Data from the `collections.generate` entry that created the page */
423
+ data?: Record<string, unknown>;
305
424
  }
306
425
  /**
307
426
  * Collection aggregation data available to index page templates.
@@ -368,8 +487,17 @@ export interface TemplateContext {
368
487
  };
369
488
  /** Discovered partials from underscore folders in hierarchy */
370
489
  partials: Record<string, string>;
371
- /** Collection data (available on index pages and child pages showing parent collection) */
490
+ /**
491
+ * Collection data: the page's own collection on index pages, the collection the page belongs
492
+ * to on other pages, or the one named by the `collection` front matter field
493
+ */
372
494
  collection?: CollectionData;
495
+ /** This page's position in a paginated listing (pages with `paginate`) */
496
+ pagination?: PaginationData;
497
+ /** Key, label and items of a page expanded from a `[param].md` template */
498
+ generated?: GeneratedPageData;
499
+ /** Converts text to the URL slug Stati uses for generated page keys and heading anchors */
500
+ slugify?: (text: string) => string;
373
501
  /** TypeScript bundle assets (available when typescript.enabled is true) */
374
502
  assets?: StatiAssets;
375
503
  /**