@stati/core 1.28.0 → 1.29.1

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 (74) hide show
  1. package/README.md +6 -1
  2. package/dist/core/build.d.ts.map +1 -1
  3. package/dist/core/build.js +192 -71
  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 +32 -93
  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/preview.d.ts.map +1 -1
  31. package/dist/core/preview.js +21 -32
  32. package/dist/core/templates.d.ts +3 -0
  33. package/dist/core/templates.d.ts.map +1 -1
  34. package/dist/core/templates.js +67 -127
  35. package/dist/core/utils/bundle-matching.utils.d.ts +8 -8
  36. package/dist/core/utils/bundle-matching.utils.d.ts.map +1 -1
  37. package/dist/core/utils/bundle-matching.utils.js +10 -10
  38. package/dist/core/utils/fs.utils.d.ts +8 -0
  39. package/dist/core/utils/fs.utils.d.ts.map +1 -1
  40. package/dist/core/utils/fs.utils.js +13 -1
  41. package/dist/core/utils/index.d.ts +6 -6
  42. package/dist/core/utils/index.d.ts.map +1 -1
  43. package/dist/core/utils/index.js +5 -5
  44. package/dist/core/utils/paths.utils.d.ts +15 -0
  45. package/dist/core/utils/paths.utils.d.ts.map +1 -1
  46. package/dist/core/utils/paths.utils.js +20 -0
  47. package/dist/core/utils/server.utils.d.ts +38 -3
  48. package/dist/core/utils/server.utils.d.ts.map +1 -1
  49. package/dist/core/utils/server.utils.js +132 -44
  50. package/dist/core/utils/template-discovery.utils.d.ts +25 -1
  51. package/dist/core/utils/template-discovery.utils.d.ts.map +1 -1
  52. package/dist/core/utils/template-discovery.utils.js +61 -11
  53. package/dist/core/utils/typescript.utils.d.ts +27 -0
  54. package/dist/core/utils/typescript.utils.d.ts.map +1 -1
  55. package/dist/core/utils/typescript.utils.js +34 -1
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/rss/generator.d.ts.map +1 -1
  59. package/dist/rss/generator.js +5 -2
  60. package/dist/search/generator.d.ts +27 -7
  61. package/dist/search/generator.d.ts.map +1 -1
  62. package/dist/search/generator.js +55 -12
  63. package/dist/search/index.d.ts +1 -1
  64. package/dist/search/index.d.ts.map +1 -1
  65. package/dist/search/index.js +1 -1
  66. package/dist/types/config.d.ts +61 -4
  67. package/dist/types/config.d.ts.map +1 -1
  68. package/dist/types/content.d.ts +129 -1
  69. package/dist/types/content.d.ts.map +1 -1
  70. package/dist/types/index.d.ts +2 -2
  71. package/dist/types/index.d.ts.map +1 -1
  72. package/dist/types/isg.d.ts +12 -0
  73. package/dist/types/isg.d.ts.map +1 -1
  74. package/package.json +1 -1
@@ -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
  /**
@@ -57,6 +57,33 @@ export interface WatchOptions {
57
57
  * @returns Array of detected bundle info
58
58
  */
59
59
  export declare function detectExistingBundles(options: Omit<CompileOptions, 'logger'>): Promise<CompiledBundleInfo[]>;
60
+ /**
61
+ * Options for {@link removeStaleBundleFiles}.
62
+ */
63
+ export interface RemoveStaleBundlesOptions {
64
+ /** Root directory of the project */
65
+ projectRoot: string;
66
+ /** TypeScript configuration */
67
+ config: TypeScriptConfig;
68
+ /** Output directory (defaults to 'dist') */
69
+ outDir?: string;
70
+ /** The bundles this build compiled */
71
+ compiled: CompiledBundleInfo[];
72
+ }
73
+ /**
74
+ * Deletes the files of compiled bundles that earlier builds left in the bundle output
75
+ * directory: hashed copies from builds whose code differed, and the unhashed file and source
76
+ * map the dev server writes, which production pages never load. Only files named after a
77
+ * bundle compiled in this build are deleted, and never a file this build wrote, so other files
78
+ * in the directory stay.
79
+ *
80
+ * The build calls this after every page is written: until then, pages from the previous
81
+ * build still load the old files.
82
+ *
83
+ * @param options - The project, its TypeScript config and output directory, and the bundles
84
+ * this build compiled
85
+ */
86
+ export declare function removeStaleBundleFiles(options: RemoveStaleBundlesOptions): Promise<void>;
60
87
  /**
61
88
  * Compile TypeScript files using esbuild.
62
89
  * Supports multiple bundles with parallel compilation.
@@ -1 +1 @@
1
- {"version":3,"file":"typescript.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/typescript.utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAInC,OAAO,EAA6B,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAQrD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,SAAS,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAuCD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,GACtC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAyC/B;AAuED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkD9F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAyHjC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB5E;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAqC7D;AAiBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CA8B7E"}
1
+ {"version":3,"file":"typescript.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/typescript.utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAInC,OAAO,EAA6B,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAQrD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,SAAS,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAuCD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,GACtC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAyC/B;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB9F;AAuED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkD9F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAyHjC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB5E;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAqC7D;AAiBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CA8B7E"}
@@ -7,7 +7,7 @@
7
7
  import * as esbuild from 'esbuild';
8
8
  import * as path from 'node:path';
9
9
  import * as fs from 'node:fs/promises';
10
- import { pathExists } from './fs.utils.js';
10
+ import { pathExists, readdir, remove } from './fs.utils.js';
11
11
  import { validateUniqueBundleNames } from './bundle-matching.utils.js';
12
12
  import { DEFAULT_TS_SRC_DIR, DEFAULT_TS_OUT_DIR, DEFAULT_BUNDLES, DEFAULT_OUT_DIR, } from '../../constants.js';
13
13
  /**
@@ -83,6 +83,39 @@ export async function detectExistingBundles(options) {
83
83
  }
84
84
  return results;
85
85
  }
86
+ /**
87
+ * Deletes the files of compiled bundles that earlier builds left in the bundle output
88
+ * directory: hashed copies from builds whose code differed, and the unhashed file and source
89
+ * map the dev server writes, which production pages never load. Only files named after a
90
+ * bundle compiled in this build are deleted, and never a file this build wrote, so other files
91
+ * in the directory stay.
92
+ *
93
+ * The build calls this after every page is written: until then, pages from the previous
94
+ * build still load the old files.
95
+ *
96
+ * @param options - The project, its TypeScript config and output directory, and the bundles
97
+ * this build compiled
98
+ */
99
+ export async function removeStaleBundleFiles(options) {
100
+ const { projectRoot, config, outDir: globalOutDir = DEFAULT_OUT_DIR, compiled } = options;
101
+ if (compiled.length === 0) {
102
+ return;
103
+ }
104
+ const outDir = path.join(projectRoot, globalOutDir, resolveConfig(config, 'production').outDir);
105
+ const current = new Set(compiled.map((bundle) => bundle.filename));
106
+ // name.js, name-HASH.js (esbuild's 8-character hash) and their source maps
107
+ const patterns = compiled.map(({ config }) => {
108
+ const name = config.bundleName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
109
+ return new RegExp(`^${name}(?:-[A-Z0-9]{8})?\\.js(?:\\.map)?$`);
110
+ });
111
+ const entries = await readdir(outDir, { withFileTypes: true }).catch(() => []);
112
+ // Files only: remove() deletes folders with their contents, and links are left alone
113
+ const stale = entries
114
+ .filter((entry) => entry.isFile())
115
+ .map((entry) => entry.name)
116
+ .filter((file) => !current.has(file) && patterns.some((pattern) => pattern.test(file)));
117
+ await Promise.all(stale.map((file) => remove(path.join(outDir, file))));
118
+ }
86
119
  /**
87
120
  * Compile a single bundle using esbuild.
88
121
  * @internal
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.
@@ -73,21 +82,32 @@ export declare function generateSearchIndex(searchablePages: SearchablePage[], c
73
82
  export declare function writeSearchIndex(searchIndex: SearchIndex, outDir: string, filename: string, skipIfUnchanged?: boolean): Promise<SearchIndexMetadata>;
74
83
  /**
75
84
  * Computes the search index filename based on configuration.
76
- * When hashFilename is true, generates a deterministic hash based on the build ID.
77
- * This allows the filename to be known before the index is generated,
78
- * enabling meta tag injection during initial template render.
85
+ * When hashFilename is true, the name carries a hash of `hashInput`. Production builds pass
86
+ * the index content, so the name changes only when the indexed text does; every page links to
87
+ * the index, so the name is part of each page's cache hash.
79
88
  *
80
89
  * @param config - Search configuration
81
- * @param buildId - Unique identifier for this build (e.g., timestamp or build ID)
90
+ * @param hashInput - The value the hash is computed from, such as the serialized documents
82
91
  * @returns The search index filename (without leading slash)
83
92
  *
84
93
  * @example
85
94
  * ```typescript
86
- * // At build start, compute the filename
87
- * const filename = computeSearchIndexFilename(config.search, Date.now().toString());
95
+ * const filename = computeSearchIndexFilename(config.search, JSON.stringify(index.documents));
88
96
  * // filename: 'search-index-a1b2c3d4.json' (when hashFilename is true)
89
97
  * // filename: 'search-index.json' (when hashFilename is false)
90
98
  * ```
91
99
  */
92
- export declare function computeSearchIndexFilename(config: SearchConfig, buildId?: string): string;
100
+ export declare function computeSearchIndexFilename(config: SearchConfig, hashInput?: string): string;
101
+ /**
102
+ * Deletes search index files that earlier builds wrote under another name: hashed files from
103
+ * builds whose indexed text differed, and the file of a build with the other `hashFilename`
104
+ * setting. Only files in the output directory named after `indexName` are deleted, never the
105
+ * current one.
106
+ *
107
+ * @param outDir - The output directory
108
+ * @param config - Search configuration
109
+ * @param currentFilename - The index file this build wrote
110
+ * @returns The deleted file names
111
+ */
112
+ export declare function removeStaleSearchIndexes(outDir: string, config: SearchConfig, currentFilename: string): Promise<string[]>;
93
113
  //# sourceMappingURL=generator.d.ts.map
@@ -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;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAW3F;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBnB"}
@@ -4,8 +4,9 @@
4
4
  */
5
5
  import { createHash } from 'node:crypto';
6
6
  import { join } from 'node:path';
7
- import { writeFile, ensureDir } from '../core/utils/fs.utils.js';
7
+ import { writeFile, ensureDir, readdir, remove } 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
  *
@@ -307,32 +327,55 @@ export async function writeSearchIndex(searchIndex, outDir, filename, skipIfUnch
307
327
  }
308
328
  /**
309
329
  * Computes the search index filename based on configuration.
310
- * When hashFilename is true, generates a deterministic hash based on the build ID.
311
- * This allows the filename to be known before the index is generated,
312
- * enabling meta tag injection during initial template render.
330
+ * When hashFilename is true, the name carries a hash of `hashInput`. Production builds pass
331
+ * the index content, so the name changes only when the indexed text does; every page links to
332
+ * the index, so the name is part of each page's cache hash.
313
333
  *
314
334
  * @param config - Search configuration
315
- * @param buildId - Unique identifier for this build (e.g., timestamp or build ID)
335
+ * @param hashInput - The value the hash is computed from, such as the serialized documents
316
336
  * @returns The search index filename (without leading slash)
317
337
  *
318
338
  * @example
319
339
  * ```typescript
320
- * // At build start, compute the filename
321
- * const filename = computeSearchIndexFilename(config.search, Date.now().toString());
340
+ * const filename = computeSearchIndexFilename(config.search, JSON.stringify(index.documents));
322
341
  * // filename: 'search-index-a1b2c3d4.json' (when hashFilename is true)
323
342
  * // filename: 'search-index.json' (when hashFilename is false)
324
343
  * ```
325
344
  */
326
- export function computeSearchIndexFilename(config, buildId) {
345
+ export function computeSearchIndexFilename(config, hashInput) {
327
346
  const indexName = config.indexName ?? SEARCH_DEFAULTS.indexName;
328
347
  const hashFilename = config.hashFilename ?? SEARCH_DEFAULTS.hashFilename;
329
348
  if (hashFilename) {
330
- // Generate a deterministic hash based on build ID
331
- // This allows the filename to be known before content is generated
332
- const hashInput = buildId ?? Date.now().toString();
333
- const hash = generateContentHash(hashInput);
349
+ const hash = generateContentHash(hashInput ?? Date.now().toString());
334
350
  return `${indexName}-${hash}.json`;
335
351
  }
336
352
  // Return base filename (without hash)
337
353
  return `${indexName}.json`;
338
354
  }
355
+ /**
356
+ * Deletes search index files that earlier builds wrote under another name: hashed files from
357
+ * builds whose indexed text differed, and the file of a build with the other `hashFilename`
358
+ * setting. Only files in the output directory named after `indexName` are deleted, never the
359
+ * current one.
360
+ *
361
+ * @param outDir - The output directory
362
+ * @param config - Search configuration
363
+ * @param currentFilename - The index file this build wrote
364
+ * @returns The deleted file names
365
+ */
366
+ export async function removeStaleSearchIndexes(outDir, config, currentFilename) {
367
+ const indexName = (config.indexName ?? SEARCH_DEFAULTS.indexName).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
368
+ // name.json and name-HASH.json (generateContentHash's 8 hex characters)
369
+ const pattern = new RegExp(`^${indexName}(?:-[0-9a-f]{8})?\\.json$`);
370
+ const entries = await readdir(outDir, { withFileTypes: true }).catch(() => null);
371
+ if (!entries) {
372
+ return [];
373
+ }
374
+ // Files only: remove() deletes folders with their contents, and links are left alone
375
+ const stale = entries
376
+ .filter((entry) => entry.isFile())
377
+ .map((entry) => entry.name)
378
+ .filter((file) => file !== currentFilename && pattern.test(file));
379
+ await Promise.all(stale.map((file) => remove(join(outDir, file))));
380
+ return stale;
381
+ }
@@ -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, removeStaleSearchIndexes, } 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,EAC1B,wBAAwB,GACzB,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, removeStaleSearchIndexes, } 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.
@@ -157,16 +212,18 @@ export interface BundleConfig {
157
212
  bundleName: string;
158
213
  /**
159
214
  * Glob patterns for pages that should include this bundle.
160
- * Matches against page output path (e.g., '/docs/api/hooks.html').
215
+ * Matches against the page URL (e.g., '/docs/api/hooks', or '/' for the home page),
216
+ * not its output file. A pattern must match the whole URL: '/docs/**' does not match
217
+ * the '/docs' index page itself.
161
218
  * If omitted, bundle is included on ALL pages (global bundle).
162
219
  *
163
- * Supports glob syntax: `*`, `**`, `?`, `[abc]`, `{a,b}`
220
+ * Supports glob syntax: `*`, `**`, `?`, `[abc]` (no `{a,b}`; list each pattern instead)
164
221
  *
165
- * @example ['/docs/**', '/api/**']
222
+ * @example ['/docs', '/docs/**', '/api/**']
166
223
  */
167
224
  include?: string[];
168
225
  /**
169
- * Glob patterns for pages to exclude from this bundle.
226
+ * Glob patterns for page URLs to exclude from this bundle.
170
227
  * Takes precedence over include patterns.
171
228
  *
172
229
  * @example ['/docs/legacy/**']
@@ -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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"}