@stati/core 1.27.0 → 1.27.2

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.
@@ -273,7 +273,7 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
273
273
  const shouldRebuild = options.force ||
274
274
  config.isg?.enabled === false ||
275
275
  !(await pathExists(outputPath)) ||
276
- (await shouldRebuildPage(page, existingEntry, config, buildTime, renderInputsHash));
276
+ (await shouldRebuildPage(page, existingEntry, config, pages, buildTime, renderInputsHash));
277
277
  const shouldRebuildTime = performance.now() - shouldRebuildStart;
278
278
  totalShouldRebuildTime += shouldRebuildTime;
279
279
  if (!shouldRebuild) {
@@ -357,10 +357,10 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
357
357
  if (!options.skipCacheUpdate) {
358
358
  const cacheEntryStart = performance.now();
359
359
  if (existingEntry) {
360
- manifest.entries[cacheKey] = await updateCacheEntry(existingEntry, page, config, buildTime, false, renderInputsHash);
360
+ manifest.entries[cacheKey] = await updateCacheEntry(existingEntry, page, config, pages, buildTime, false, renderInputsHash);
361
361
  }
362
362
  else {
363
- manifest.entries[cacheKey] = await createCacheEntry(page, config, buildTime, renderInputsHash);
363
+ manifest.entries[cacheKey] = await createCacheEntry(page, config, pages, buildTime, renderInputsHash);
364
364
  }
365
365
  totalCacheEntryTime += performance.now() - cacheEntryStart;
366
366
  }
@@ -393,11 +393,11 @@ async function processPagesWithCache(pages, manifest, config, outDir, md, eta, n
393
393
  for (const { cacheKey, page, existingEntry, renderInputsHash } of pendingCacheUpdates) {
394
394
  if (existingEntry) {
395
395
  // Fast update: reuse existing deps, just update hashes and timestamp
396
- manifest.entries[cacheKey] = await updateCacheEntry(existingEntry, page, config, buildTime, true, // fastUpdate = true in dev mode
396
+ manifest.entries[cacheKey] = await updateCacheEntry(existingEntry, page, config, pages, buildTime, true, // fastUpdate = true in dev mode
397
397
  renderInputsHash);
398
398
  }
399
399
  else {
400
- manifest.entries[cacheKey] = await createCacheEntry(page, config, buildTime, renderInputsHash);
400
+ manifest.entries[cacheKey] = await createCacheEntry(page, config, pages, buildTime, renderInputsHash);
401
401
  }
402
402
  }
403
403
  totalCacheEntryTime = performance.now() - cacheEntryStart;
@@ -1 +1 @@
1
- {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/core/content.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,OAAO,GACtB,OAAO,CAAC,SAAS,EAAE,CAAC,CA6CtB"}
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/core/content.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,OAAO,GACtB,OAAO,CAAC,SAAS,EAAE,CAAC,CAgDtB"}
@@ -1,5 +1,5 @@
1
1
  import glob from 'fast-glob';
2
- import { readFile, resolveSrcDir } from './utils/index.js';
2
+ import { readFile, resolveSrcDir, getFrontMatterDate } from './utils/index.js';
3
3
  import matter from 'gray-matter';
4
4
  import { relative, dirname, basename } from 'node:path';
5
5
  import { MARKDOWN_EXTENSION } from '../constants.js';
@@ -49,8 +49,11 @@ export async function loadContent(config, includeDrafts) {
49
49
  frontMatter,
50
50
  content: markdown,
51
51
  };
52
- if (frontMatter.publishedAt && typeof frontMatter.publishedAt === 'string') {
53
- page.publishedAt = new Date(frontMatter.publishedAt);
52
+ const publishedAt = getFrontMatterDate(frontMatter, (field, value) => {
53
+ console.warn(`Ignoring invalid date in "${field}" of ${file}: ${describeDateValue(value)}`);
54
+ });
55
+ if (publishedAt) {
56
+ page.publishedAt = publishedAt;
54
57
  }
55
58
  pages.push(page);
56
59
  }
@@ -64,6 +67,19 @@ function computeSlug(relativePath) {
64
67
  }
65
68
  return dir === '.' ? `/${name}` : `/${dir}/${name}`;
66
69
  }
70
+ /**
71
+ * Describes a rejected front matter date for a warning without calling the value's
72
+ * own conversion methods, which a YAML mapping such as `{ toString: x }` can break.
73
+ */
74
+ function describeDateValue(value) {
75
+ if (typeof value === 'string') {
76
+ return JSON.stringify(value);
77
+ }
78
+ if (typeof value === 'object' && !(value instanceof Date)) {
79
+ return Array.isArray(value) ? 'a list' : 'a mapping';
80
+ }
81
+ return String(value);
82
+ }
67
83
  function computeUrl(slug, frontMatter) {
68
84
  if (frontMatter.permalink && typeof frontMatter.permalink === 'string') {
69
85
  return frontMatter.permalink;
@@ -7,34 +7,36 @@ import type { PageModel, CacheEntry, StatiConfig } from '../../types/index.js';
7
7
  * @param page - The page model to check
8
8
  * @param entry - Existing cache entry, or undefined if not cached
9
9
  * @param config - Stati configuration
10
+ * @param allPages - All pages in the site, used to resolve the same layout as rendering
10
11
  * @param now - Current date/time
11
12
  * @param renderInputsHash - Optional hash of asset URLs and injection settings
12
13
  * @returns True if the page should be rebuilt
13
14
  *
14
15
  * @example
15
16
  * ```typescript
16
- * const shouldRebuild = await shouldRebuildPage(page, cacheEntry, config, new Date());
17
+ * const shouldRebuild = await shouldRebuildPage(page, cacheEntry, config, pages, new Date());
17
18
  * if (shouldRebuild) {
18
19
  * console.log('Page needs rebuilding');
19
20
  * }
20
21
  * ```
21
22
  */
22
- export declare function shouldRebuildPage(page: PageModel, entry: CacheEntry | undefined, config: StatiConfig, now: Date, renderInputsHash?: string): Promise<boolean>;
23
+ export declare function shouldRebuildPage(page: PageModel, entry: CacheEntry | undefined, config: StatiConfig, allPages: PageModel[], now: Date, renderInputsHash?: string): Promise<boolean>;
23
24
  /**
24
25
  * Creates a new cache entry for a page after it has been rendered.
25
26
  *
26
27
  * @param page - The page model
27
28
  * @param config - Stati configuration
29
+ * @param allPages - All pages in the site, used to resolve the same layout as rendering
28
30
  * @param renderedAt - When the page was rendered
29
31
  * @param renderInputsHash - Optional hash of asset URLs and injection settings
30
32
  * @returns New cache entry
31
33
  *
32
34
  * @example
33
35
  * ```typescript
34
- * const entry = await createCacheEntry(page, config, new Date());
36
+ * const entry = await createCacheEntry(page, config, pages, new Date());
35
37
  * ```
36
38
  */
37
- export declare function createCacheEntry(page: PageModel, config: StatiConfig, renderedAt: Date, renderInputsHash?: string): Promise<CacheEntry>;
39
+ export declare function createCacheEntry(page: PageModel, config: StatiConfig, allPages: PageModel[], renderedAt: Date, renderInputsHash?: string): Promise<CacheEntry>;
38
40
  /**
39
41
  * Updates an existing cache entry with new information after rebuilding.
40
42
  * In dev mode, this is optimized to reuse the existing deps array since
@@ -43,6 +45,7 @@ export declare function createCacheEntry(page: PageModel, config: StatiConfig, r
43
45
  * @param entry - Existing cache entry
44
46
  * @param page - The page model
45
47
  * @param config - Stati configuration
48
+ * @param allPages - All pages in the site, used to resolve the same layout as rendering
46
49
  * @param renderedAt - When the page was rendered
47
50
  * @param fastUpdate - If true, skip expensive dep tracking (dev mode optimization)
48
51
  * @param renderInputsHash - Optional hash of asset URLs and injection settings
@@ -50,8 +53,8 @@ export declare function createCacheEntry(page: PageModel, config: StatiConfig, r
50
53
  *
51
54
  * @example
52
55
  * ```typescript
53
- * const updatedEntry = await updateCacheEntry(existingEntry, page, config, new Date());
56
+ * const updatedEntry = await updateCacheEntry(existingEntry, page, config, pages, new Date());
54
57
  * ```
55
58
  */
56
- export declare function updateCacheEntry(entry: CacheEntry, page: PageModel, config: StatiConfig, renderedAt: Date, fastUpdate?: boolean, renderInputsHash?: string): Promise<CacheEntry>;
59
+ export declare function updateCacheEntry(entry: CacheEntry, page: PageModel, config: StatiConfig, allPages: PageModel[], renderedAt: Date, fastUpdate?: boolean, renderInputsHash?: string): Promise<CacheEntry>;
57
60
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../src/core/isg/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAoE/E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,IAAI,EACT,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,OAAO,CAAC,CAoKlB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,IAAI,EAChB,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,UAAU,CAAC,CAqFrB;AAkDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,IAAI,EAChB,UAAU,UAAQ,EAClB,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,UAAU,CAAC,CA4CrB"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../src/core/isg/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAqE/E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,SAAS,EAAE,EACrB,GAAG,EAAE,IAAI,EACT,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,OAAO,CAAC,CAoKlB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,SAAS,EAAE,EACrB,UAAU,EAAE,IAAI,EAChB,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,UAAU,CAAC,CAqFrB;AAkDD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,SAAS,EAAE,EACrB,UAAU,EAAE,IAAI,EAChB,UAAU,UAAQ,EAClB,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,UAAU,CAAC,CAmDrB"}
@@ -1,7 +1,8 @@
1
1
  import { computeContentHash, computeFileHash, computeInputsHash } from './hash.js';
2
- import { trackTemplateDependencies } from './deps.js';
2
+ import { trackTemplateDependencies, resolveLayoutDependency } from './deps.js';
3
3
  import { computeEffectiveTTL, computeNextRebuildAt, isPageFrozen } from './ttl.js';
4
4
  import { validatePageISGOverrides, extractNumericOverride } from './validation.js';
5
+ import { getFrontMatterDate } from '../utils/index.js';
5
6
  import { performance } from 'node:perf_hooks';
6
7
  /**
7
8
  * Determines the output path for a page.
@@ -64,19 +65,20 @@ function isValidCacheEntry(entry) {
64
65
  * @param page - The page model to check
65
66
  * @param entry - Existing cache entry, or undefined if not cached
66
67
  * @param config - Stati configuration
68
+ * @param allPages - All pages in the site, used to resolve the same layout as rendering
67
69
  * @param now - Current date/time
68
70
  * @param renderInputsHash - Optional hash of asset URLs and injection settings
69
71
  * @returns True if the page should be rebuilt
70
72
  *
71
73
  * @example
72
74
  * ```typescript
73
- * const shouldRebuild = await shouldRebuildPage(page, cacheEntry, config, new Date());
75
+ * const shouldRebuild = await shouldRebuildPage(page, cacheEntry, config, pages, new Date());
74
76
  * if (shouldRebuild) {
75
77
  * console.log('Page needs rebuilding');
76
78
  * }
77
79
  * ```
78
80
  */
79
- export async function shouldRebuildPage(page, entry, config, now, renderInputsHash) {
81
+ export async function shouldRebuildPage(page, entry, config, allPages, now, renderInputsHash) {
80
82
  if (config.isg?.enabled === false) {
81
83
  return true;
82
84
  }
@@ -95,7 +97,7 @@ export async function shouldRebuildPage(page, entry, config, now, renderInputsHa
95
97
  // Track dependencies with error handling
96
98
  let deps;
97
99
  try {
98
- deps = await trackTemplateDependencies(page, config);
100
+ deps = await trackTemplateDependencies(page, config, allPages);
99
101
  }
100
102
  catch (error) {
101
103
  if (error instanceof Error && error.name === 'CircularDependencyError') {
@@ -210,16 +212,17 @@ export async function shouldRebuildPage(page, entry, config, now, renderInputsHa
210
212
  *
211
213
  * @param page - The page model
212
214
  * @param config - Stati configuration
215
+ * @param allPages - All pages in the site, used to resolve the same layout as rendering
213
216
  * @param renderedAt - When the page was rendered
214
217
  * @param renderInputsHash - Optional hash of asset URLs and injection settings
215
218
  * @returns New cache entry
216
219
  *
217
220
  * @example
218
221
  * ```typescript
219
- * const entry = await createCacheEntry(page, config, new Date());
222
+ * const entry = await createCacheEntry(page, config, pages, new Date());
220
223
  * ```
221
224
  */
222
- export async function createCacheEntry(page, config, renderedAt, renderInputsHash) {
225
+ export async function createCacheEntry(page, config, allPages, renderedAt, renderInputsHash) {
223
226
  const timings = {};
224
227
  let start = performance.now();
225
228
  // Validate page-level ISG overrides first
@@ -231,7 +234,7 @@ export async function createCacheEntry(page, config, renderedAt, renderInputsHas
231
234
  timings.contentHash = performance.now() - start;
232
235
  // Track all template dependencies
233
236
  start = performance.now();
234
- const deps = await trackTemplateDependencies(page, config);
237
+ const deps = await trackTemplateDependencies(page, config, allPages);
235
238
  timings.trackDeps = performance.now() - start;
236
239
  // Compute hashes for all dependencies
237
240
  start = performance.now();
@@ -338,6 +341,7 @@ async function hasTemplateStructureChanged(entry) {
338
341
  * @param entry - Existing cache entry
339
342
  * @param page - The page model
340
343
  * @param config - Stati configuration
344
+ * @param allPages - All pages in the site, used to resolve the same layout as rendering
341
345
  * @param renderedAt - When the page was rendered
342
346
  * @param fastUpdate - If true, skip expensive dep tracking (dev mode optimization)
343
347
  * @param renderInputsHash - Optional hash of asset URLs and injection settings
@@ -345,19 +349,24 @@ async function hasTemplateStructureChanged(entry) {
345
349
  *
346
350
  * @example
347
351
  * ```typescript
348
- * const updatedEntry = await updateCacheEntry(existingEntry, page, config, new Date());
352
+ * const updatedEntry = await updateCacheEntry(existingEntry, page, config, pages, new Date());
349
353
  * ```
350
354
  */
351
- export async function updateCacheEntry(entry, page, config, renderedAt, fastUpdate = false, renderInputsHash) {
355
+ export async function updateCacheEntry(entry, page, config, allPages, renderedAt, fastUpdate = false, renderInputsHash) {
352
356
  // In fast update mode (dev), reuse existing deps to avoid expensive tracking
353
357
  if (fastUpdate && entry.deps.length > 0) {
354
358
  // Detect if template structure has changed (new includes/extends added)
355
359
  // by checking if the layout file has been modified or if template content
356
360
  // contains different dependency patterns
357
361
  const structureChanged = await hasTemplateStructureChanged(entry);
358
- if (structureChanged) {
359
- // Template structure changed - fall through to full dependency tracking
360
- // This ensures we pick up new includes/extends
362
+ // The page can also switch layouts without any template file changing, for example
363
+ // when a collection gains its first child page or a front matter layout is edited.
364
+ // deps[0] is always the layout, so compare it against the layout used now.
365
+ const layoutChanged = !structureChanged &&
366
+ (await resolveLayoutDependency(page, config, allPages)) !== entry.deps[0];
367
+ if (structureChanged || layoutChanged) {
368
+ // Template structure or layout changed - fall through to full dependency tracking
369
+ // This ensures we pick up new includes/extends and the newly selected layout
361
370
  }
362
371
  else {
363
372
  // Structure unchanged - safe to reuse existing deps
@@ -380,7 +389,7 @@ export async function updateCacheEntry(entry, page, config, renderedAt, fastUpda
380
389
  }
381
390
  }
382
391
  // Full update - create new entry and preserve original publishedAt
383
- const newEntry = await createCacheEntry(page, config, renderedAt, renderInputsHash);
392
+ const newEntry = await createCacheEntry(page, config, allPages, renderedAt, renderInputsHash);
384
393
  // Preserve original publishedAt if no new one is specified
385
394
  if (!newEntry.publishedAt && entry.publishedAt) {
386
395
  newEntry.publishedAt = entry.publishedAt;
@@ -391,24 +400,5 @@ export async function updateCacheEntry(entry, page, config, renderedAt, fastUpda
391
400
  * Helper function to get published date as ISO string from page front matter.
392
401
  */
393
402
  function getPublishedDateISO(page) {
394
- const frontMatter = page.frontMatter;
395
- // Try common field names for published date
396
- const dateFields = ['publishedAt', 'published', 'date', 'createdAt'];
397
- for (const field of dateFields) {
398
- const value = frontMatter[field];
399
- if (value) {
400
- // Handle string dates
401
- if (typeof value === 'string') {
402
- const date = new Date(value);
403
- if (!isNaN(date.getTime())) {
404
- return date.toISOString();
405
- }
406
- }
407
- // Handle Date objects
408
- if (value instanceof Date && !isNaN(value.getTime())) {
409
- return value.toISOString();
410
- }
411
- }
412
- }
413
- return undefined;
403
+ return getFrontMatterDate(page.frontMatter)?.toISOString();
414
404
  }
@@ -22,13 +22,14 @@ export declare class CircularDependencyError extends Error {
22
22
  *
23
23
  * @param page - The page model to track dependencies for
24
24
  * @param config - Stati configuration
25
+ * @param allPages - All pages in the site, used to detect collection index pages the same way rendering does
25
26
  * @returns Array of absolute paths to actually-used template files (POSIX format)
26
27
  * @throws {CircularDependencyError} When circular dependencies are detected in templates
27
28
  *
28
29
  * @example
29
30
  * ```typescript
30
31
  * try {
31
- * const deps = await trackTemplateDependencies(page, config);
32
+ * const deps = await trackTemplateDependencies(page, config, allPages);
32
33
  * console.log(`Page depends on ${deps.length} template files`);
33
34
  * } catch (error) {
34
35
  * if (error instanceof CircularDependencyError) {
@@ -37,7 +38,18 @@ export declare class CircularDependencyError extends Error {
37
38
  * }
38
39
  * ```
39
40
  */
40
- export declare function trackTemplateDependencies(page: PageModel, config: StatiConfig): Promise<string[]>;
41
+ export declare function trackTemplateDependencies(page: PageModel, config: StatiConfig, allPages: PageModel[]): Promise<string[]>;
42
+ /**
43
+ * Resolves the layout a page renders with, in the form stored as the first entry of its
44
+ * cache dependencies. Uses the same lookup as rendering, so callers can tell whether the
45
+ * page's layout has changed since its dependencies were recorded.
46
+ *
47
+ * @param page - The page model to resolve the layout for
48
+ * @param config - Stati configuration
49
+ * @param allPages - All pages in the site, used to detect collection index pages the same way rendering does
50
+ * @returns Absolute path to the layout file (POSIX format), or null if no layout applies
51
+ */
52
+ export declare function resolveLayoutDependency(page: PageModel, config: StatiConfig, allPages: PageModel[]): Promise<string | null>;
41
53
  /**
42
54
  * Resolves a template name to its file path.
43
55
  * Used for explicit layout specifications in front matter.
@@ -1 +1 @@
1
- {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../../src/core/isg/deps.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAenE;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aAE9B,eAAe,EAAE,MAAM,EAAE;gBAAzB,eAAe,EAAE,MAAM,EAAE,EACzC,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CA+CnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB"}
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../../src/core/isg/deps.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAenE;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;aAE9B,eAAe,EAAE,MAAM,EAAE;gBAAzB,eAAe,EAAE,MAAM,EAAE,EACzC,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,SAAS,EAAE,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAuCnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,SAAS,EAAE,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAexB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB"}
@@ -42,13 +42,14 @@ export class CircularDependencyError extends Error {
42
42
  *
43
43
  * @param page - The page model to track dependencies for
44
44
  * @param config - Stati configuration
45
+ * @param allPages - All pages in the site, used to detect collection index pages the same way rendering does
45
46
  * @returns Array of absolute paths to actually-used template files (POSIX format)
46
47
  * @throws {CircularDependencyError} When circular dependencies are detected in templates
47
48
  *
48
49
  * @example
49
50
  * ```typescript
50
51
  * try {
51
- * const deps = await trackTemplateDependencies(page, config);
52
+ * const deps = await trackTemplateDependencies(page, config, allPages);
52
53
  * console.log(`Page depends on ${deps.length} template files`);
53
54
  * } catch (error) {
54
55
  * if (error instanceof CircularDependencyError) {
@@ -57,7 +58,7 @@ export class CircularDependencyError extends Error {
57
58
  * }
58
59
  * ```
59
60
  */
60
- export async function trackTemplateDependencies(page, config) {
61
+ export async function trackTemplateDependencies(page, config, allPages) {
61
62
  // Early return if required config values are missing
62
63
  if (!config.srcDir) {
63
64
  console.warn('Config srcDir is missing, cannot track template dependencies');
@@ -65,16 +66,13 @@ export async function trackTemplateDependencies(page, config) {
65
66
  }
66
67
  const deps = [];
67
68
  const srcDir = resolveSrcDir(config);
68
- const relativePath = relative(srcDir, page.sourcePath);
69
69
  // Track dependencies with circular detection
70
70
  // The visited set will contain all templates that are actually used (not just accessible)
71
71
  const visited = new Set();
72
72
  const currentPath = new Set();
73
73
  // 1. Find the layout file that will be used for this page
74
- const layoutPath = await discoverLayout(relativePath, config, page.frontMatter.layout, isCollectionIndexPage(page));
75
- if (layoutPath) {
76
- // Normalize to POSIX format for consistent manifest output across platforms
77
- const absoluteLayoutPath = posix.join(srcDir.replace(/\\/g, '/'), layoutPath);
74
+ const absoluteLayoutPath = await resolveLayoutDependency(page, config, allPages);
75
+ if (absoluteLayoutPath) {
78
76
  deps.push(absoluteLayoutPath);
79
77
  // Recursively traverse the template dependency tree
80
78
  // This populates 'visited' with all actually-used templates (not just accessible ones)
@@ -93,6 +91,25 @@ export async function trackTemplateDependencies(page, config) {
93
91
  }
94
92
  return deps;
95
93
  }
94
+ /**
95
+ * Resolves the layout a page renders with, in the form stored as the first entry of its
96
+ * cache dependencies. Uses the same lookup as rendering, so callers can tell whether the
97
+ * page's layout has changed since its dependencies were recorded.
98
+ *
99
+ * @param page - The page model to resolve the layout for
100
+ * @param config - Stati configuration
101
+ * @param allPages - All pages in the site, used to detect collection index pages the same way rendering does
102
+ * @returns Absolute path to the layout file (POSIX format), or null if no layout applies
103
+ */
104
+ export async function resolveLayoutDependency(page, config, allPages) {
105
+ if (!config.srcDir) {
106
+ return null;
107
+ }
108
+ const srcDir = resolveSrcDir(config);
109
+ const layoutPath = await discoverLayout(relative(srcDir, page.sourcePath), config, page.frontMatter.layout, isCollectionIndexPage(page, allPages));
110
+ // Normalize to POSIX format for consistent manifest output across platforms
111
+ return layoutPath ? posix.join(srcDir.replace(/\\/g, '/'), layoutPath) : null;
112
+ }
96
113
  /**
97
114
  * Resolves a template name to its file path.
98
115
  * Used for explicit layout specifications in front matter.
@@ -1 +1 @@
1
- {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../../src/core/isg/ttl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAQxF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,CAW5D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAqB5E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,SAAS,EACpB,WAAW,CAAC,EAAE,IAAI,GACjB,MAAM,CAkBR;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,GAAG,EAAE,IAAI,CAAC;IACV,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,IAAI,GAAG,IAAI,CAqBd;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAiBlE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,IAAI,EACjB,UAAU,EAAE,SAAS,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,IAAI,GACR,MAAM,CAyBR"}
1
+ {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../../src/core/isg/ttl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AASxF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,CAW5D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAqB5E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,SAAS,EACpB,WAAW,CAAC,EAAE,IAAI,GACjB,MAAM,CAkBR;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,GAAG,EAAE,IAAI,CAAC;IACV,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,IAAI,GAAG,IAAI,CAqBd;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAiBlE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,IAAI,EACjB,UAAU,EAAE,SAAS,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,IAAI,GACR,MAAM,CAyBR"}
@@ -1,4 +1,5 @@
1
1
  import { CLOCK_DRIFT_TOLERANCE_MS, DEFAULT_TTL_SECONDS, MILLISECONDS_PER_DAY, } from '../../constants.js';
2
+ import { getFrontMatterDate } from '../utils/index.js';
2
3
  // dedup: time constants centralized in constants.ts
3
4
  /**
4
5
  * Safely gets the current UTC time with drift protection.
@@ -189,31 +190,8 @@ export function applyAgingRules(publishedAt, agingRules, defaultTTL, now) {
189
190
  }
190
191
  /**
191
192
  * Helper function to extract published date from page front matter.
192
- * Supports various date formats and field names.
193
- * Uses safe date parsing to handle timezone issues.
193
+ * Uses the shared field order from {@link getFrontMatterDate}.
194
194
  */
195
195
  function getPublishedDate(page) {
196
- const frontMatter = page.frontMatter;
197
- // Try common field names for published date
198
- const dateFields = ['publishedAt', 'published', 'date', 'createdAt'];
199
- for (const field of dateFields) {
200
- const value = frontMatter[field];
201
- if (value) {
202
- // Handle string dates
203
- if (typeof value === 'string') {
204
- const safeDate = parseSafeDate(value, `front-matter field "${field}" in ${page.sourcePath}`);
205
- if (safeDate) {
206
- return safeDate;
207
- }
208
- }
209
- // Handle Date objects
210
- if (value instanceof Date) {
211
- const safeDate = getSafeCurrentTime(value);
212
- if (!isNaN(safeDate.getTime())) {
213
- return safeDate;
214
- }
215
- }
216
- }
217
- }
218
- return null;
196
+ return getFrontMatterDate(page.frontMatter) ?? null;
219
197
  }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Date utilities for reading publish dates from front matter.
3
+ * @module core/utils/date
4
+ */
5
+ /**
6
+ * Front matter fields checked for a page's publish date, in priority order.
7
+ * Collections, navigation, ISG aging and RSS all use this order.
8
+ */
9
+ export declare const PUBLISHED_DATE_FIELDS: readonly ["publishedAt", "published", "date", "createdAt"];
10
+ /**
11
+ * Returns the publish date from front matter, or `undefined` if none is usable.
12
+ *
13
+ * Checks {@link PUBLISHED_DATE_FIELDS} in order and returns the first valid value.
14
+ * Accepts strings (quoted YAML dates) and `Date` objects (unquoted YAML dates,
15
+ * which gray-matter parses into `Date`). Missing, `null` and empty values are skipped;
16
+ * other invalid values are reported through `onInvalid` and skipped, so a bad
17
+ * `publishedAt` does not hide a valid `date`.
18
+ *
19
+ * @param frontMatter - Parsed front matter of a page; `null`/`undefined` returns `undefined`
20
+ * @param onInvalid - Called for each present field whose value is not a valid date
21
+ * @returns A new `Date` instance, or `undefined`
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * getFrontMatterDate({ date: new Date('2026-01-01') }); // 2026-01-01T00:00:00.000Z
26
+ * getFrontMatterDate({ publishedAt: 'soon', date: '2026-06-01' }); // 2026-06-01T00:00:00.000Z
27
+ * getFrontMatterDate({ title: 'Undated' }); // undefined
28
+ * ```
29
+ */
30
+ export declare function getFrontMatterDate(frontMatter: Record<string, unknown> | null | undefined, onInvalid?: (field: string, value: unknown) => void): Date | undefined;
31
+ //# sourceMappingURL=date.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/date.utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,eAAO,MAAM,qBAAqB,4DAA6D,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EACvD,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,GAClD,IAAI,GAAG,SAAS,CA0BlB"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Date utilities for reading publish dates from front matter.
3
+ * @module core/utils/date
4
+ */
5
+ /**
6
+ * Front matter fields checked for a page's publish date, in priority order.
7
+ * Collections, navigation, ISG aging and RSS all use this order.
8
+ */
9
+ export const PUBLISHED_DATE_FIELDS = ['publishedAt', 'published', 'date', 'createdAt'];
10
+ /**
11
+ * Returns the publish date from front matter, or `undefined` if none is usable.
12
+ *
13
+ * Checks {@link PUBLISHED_DATE_FIELDS} in order and returns the first valid value.
14
+ * Accepts strings (quoted YAML dates) and `Date` objects (unquoted YAML dates,
15
+ * which gray-matter parses into `Date`). Missing, `null` and empty values are skipped;
16
+ * other invalid values are reported through `onInvalid` and skipped, so a bad
17
+ * `publishedAt` does not hide a valid `date`.
18
+ *
19
+ * @param frontMatter - Parsed front matter of a page; `null`/`undefined` returns `undefined`
20
+ * @param onInvalid - Called for each present field whose value is not a valid date
21
+ * @returns A new `Date` instance, or `undefined`
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * getFrontMatterDate({ date: new Date('2026-01-01') }); // 2026-01-01T00:00:00.000Z
26
+ * getFrontMatterDate({ publishedAt: 'soon', date: '2026-06-01' }); // 2026-06-01T00:00:00.000Z
27
+ * getFrontMatterDate({ title: 'Undated' }); // undefined
28
+ * ```
29
+ */
30
+ export function getFrontMatterDate(frontMatter, onInvalid) {
31
+ if (!frontMatter) {
32
+ return undefined;
33
+ }
34
+ for (const field of PUBLISHED_DATE_FIELDS) {
35
+ const value = frontMatter[field];
36
+ if (value === undefined || value === null || (typeof value === 'string' && !value.trim())) {
37
+ continue;
38
+ }
39
+ let date;
40
+ if (typeof value === 'string') {
41
+ date = new Date(value);
42
+ }
43
+ else if (value instanceof Date) {
44
+ date = new Date(value.getTime());
45
+ }
46
+ if (date && !isNaN(date.getTime())) {
47
+ return date;
48
+ }
49
+ onInvalid?.(field, value);
50
+ }
51
+ return undefined;
52
+ }
@@ -27,6 +27,7 @@ export { createFallbackLogger } from './logger.utils.js';
27
27
  export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, } from './typescript.utils.js';
28
28
  export type { CompileOptions, WatchOptions } from './typescript.utils.js';
29
29
  export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
30
+ export { getFrontMatterDate, PUBLISHED_DATE_FIELDS } from './date.utils.js';
30
31
  export { isEnabledEnvFlag } from './env.utils.js';
31
32
  export { discoverOutputCssFiles, refreshCssOutputWatcher } from './dev-css-watcher.utils.js';
32
33
  export type { RefreshCssOutputWatcherParams } from './dev-css-watcher.utils.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,GACL,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACrB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,EACX,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,GACL,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACrB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,EACX,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG/E,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC"}
@@ -42,6 +42,8 @@ export { createFallbackLogger } from './logger.utils.js';
42
42
  export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, } from './typescript.utils.js';
43
43
  // HTML manipulation utilities
44
44
  export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
45
+ // Date utilities
46
+ export { getFrontMatterDate, PUBLISHED_DATE_FIELDS } from './date.utils.js';
45
47
  // Environment utilities
46
48
  export { isEnabledEnvFlag } from './env.utils.js';
47
49
  // Dev-server CSS watcher utilities
@@ -1,17 +1,19 @@
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
2
  /**
7
3
  * Determines if the given page is a collection index page.
8
4
  * A collection index page is one whose URL matches a directory path that contains other pages.
9
5
  *
6
+ * Rendering and ISG dependency tracking must both call this with the same page list,
7
+ * otherwise they can pick different layouts for the same page.
8
+ *
9
+ * Page URLs are read once per `allPages` array, at the first call; see
10
+ * `directoryPrefixCache` for when changes to the pages are picked up.
11
+ *
10
12
  * @param page - The page to check
11
- * @param allPages - All pages in the site (optional for simplified check)
13
+ * @param allPages - All pages in the site (treated as unchanging after the first call)
12
14
  * @returns True if the page is a collection index page
13
15
  */
14
- export declare function isCollectionIndexPage(page: PageModel, allPages?: PageModel[]): boolean;
16
+ export declare function isCollectionIndexPage(page: PageModel, allPages: PageModel[]): boolean;
15
17
  /**
16
18
  * Discovers layout files by searching up the directory hierarchy.
17
19
  * Supports both explicit layout specification and automatic discovery.
@@ -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;AAInE;;;GAGG;AAEH;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,OAAO,CAkBtF;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;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"}
@@ -3,15 +3,51 @@ import { pathExists } from './fs.utils.js';
3
3
  import { LAYOUT_TEMPLATE } from '../../constants.js';
4
4
  import { resolveSrcDir } from './paths.utils.js';
5
5
  /**
6
- * Shared template discovery utilities.
7
- * Extracted from templates.ts and isg/deps.ts to eliminate duplication.
6
+ * Directory prefix index per page list, so a build computes it once instead of
7
+ * scanning every page for every page.
8
+ *
9
+ * The index is a snapshot of the page URLs taken at the first lookup for a given array,
10
+ * and every later lookup for that array reuses it. In a build the first lookup happens
11
+ * in the render loop, after the `beforeAll` hook, so URL changes made there are included.
12
+ * Changes made to the same array or its pages after that are not seen; a fresh build
13
+ * loads a new array and gets a new snapshot. Taking one snapshot keeps rendering and
14
+ * ISG dependency tracking in agreement for the whole build.
8
15
  */
16
+ const directoryPrefixCache = new WeakMap();
17
+ function getDirectoryPrefixIndex(allPages) {
18
+ const cached = directoryPrefixCache.get(allPages);
19
+ if (cached) {
20
+ return cached;
21
+ }
22
+ const index = new Map();
23
+ for (const { url } of allPages) {
24
+ // Every prefix ending at a '/' is a directory this URL lives in (or is, for trailing-slash URLs)
25
+ for (let i = url.indexOf('/'); i !== -1; i = url.indexOf('/', i + 1)) {
26
+ const prefix = url.slice(0, i + 1);
27
+ const existing = index.get(prefix);
28
+ if (existing === undefined) {
29
+ index.set(prefix, url);
30
+ }
31
+ else if (existing !== null && existing !== url) {
32
+ index.set(prefix, null);
33
+ }
34
+ }
35
+ }
36
+ directoryPrefixCache.set(allPages, index);
37
+ return index;
38
+ }
9
39
  /**
10
40
  * Determines if the given page is a collection index page.
11
41
  * A collection index page is one whose URL matches a directory path that contains other pages.
12
42
  *
43
+ * Rendering and ISG dependency tracking must both call this with the same page list,
44
+ * otherwise they can pick different layouts for the same page.
45
+ *
46
+ * Page URLs are read once per `allPages` array, at the first call; see
47
+ * `directoryPrefixCache` for when changes to the pages are picked up.
48
+ *
13
49
  * @param page - The page to check
14
- * @param allPages - All pages in the site (optional for simplified check)
50
+ * @param allPages - All pages in the site (treated as unchanging after the first call)
15
51
  * @returns True if the page is a collection index page
16
52
  */
17
53
  export function isCollectionIndexPage(page, allPages) {
@@ -19,15 +55,10 @@ export function isCollectionIndexPage(page, allPages) {
19
55
  if (page.url === '/') {
20
56
  return true;
21
57
  }
22
- // If we have all pages, do a proper check
23
- if (allPages) {
24
- // Check if this page's URL is a directory path that contains other pages
25
- const pageUrlAsDir = page.url.endsWith('/') ? page.url : page.url + '/';
26
- return allPages.some((otherPage) => otherPage.url !== page.url && otherPage.url.startsWith(pageUrlAsDir));
27
- }
28
- // Simplified version when we don't have all pages
29
- // Assume any page ending in /index or at root is a collection page
30
- return page.url.endsWith('/index') || page.slug === 'index';
58
+ // The page is an index when a different page's URL lives under this page's URL as a directory
59
+ const pageUrlAsDir = page.url.endsWith('/') ? page.url : page.url + '/';
60
+ const containingUrl = getDirectoryPrefixIndex(allPages).get(pageUrlAsDir);
61
+ return containingUrl === null || (containingUrl !== undefined && containingUrl !== page.url);
31
62
  }
32
63
  /**
33
64
  * Discovers layout files by searching up the directory hierarchy.
@@ -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;AA2KlD;;;;;;;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;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"}
@@ -4,6 +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
8
  /**
8
9
  * Formats a date for RSS pubDate field (RFC 822)
9
10
  * @param date - Date to format
@@ -59,7 +60,7 @@ function filterPages(pages, feedConfig) {
59
60
  * @returns Date value or undefined
60
61
  */
61
62
  function getPageDate(page) {
62
- return page.frontMatter.publishedAt || page.frontMatter.date;
63
+ return getFrontMatterDate(page.frontMatter);
63
64
  }
64
65
  /**
65
66
  * Sorts pages based on feed configuration
@@ -85,7 +86,7 @@ function sortPages(pages, feedConfig) {
85
86
  return 1;
86
87
  if (!dateB)
87
88
  return -1;
88
- const comparison = new Date(dateA).getTime() - new Date(dateB).getTime();
89
+ const comparison = dateA.getTime() - dateB.getTime();
89
90
  return sortBy === 'date-asc' ? comparison : -comparison;
90
91
  }
91
92
  case 'title-asc':
@@ -254,8 +255,9 @@ export function generateRSSFeed(pages, config, feedConfig, logger) {
254
255
  }
255
256
  }
256
257
  // Pub date
257
- const pubDate = getFieldValue(page, itemMapping.pubDate, 'publishedAt', logger) ||
258
- getFieldValue(page, itemMapping.pubDate, 'date', logger);
258
+ const pubDate = itemMapping.pubDate
259
+ ? getFieldValue(page, itemMapping.pubDate, undefined, logger)
260
+ : getPageDate(page);
259
261
  const formattedPubDate = parseAndFormatDate(pubDate);
260
262
  if (formattedPubDate) {
261
263
  xmlLines.push(` <pubDate>${formattedPubDate}</pubDate>`);
@@ -79,10 +79,13 @@ export interface FrontMatter {
79
79
  layout?: string;
80
80
  /** Numeric order for sorting (useful for navigation) */
81
81
  order?: number;
82
- /** Publication date as ISO string */
83
- publishedAt?: string;
84
- /** Publication date (alias for publishedAt) */
85
- date?: string;
82
+ /**
83
+ * Publication date. A quoted YAML value is a string; an unquoted one
84
+ * (`publishedAt: 2024-01-01`) is parsed into a `Date`.
85
+ */
86
+ publishedAt?: string | Date;
87
+ /** Publication date, used when `publishedAt` and `published` are absent or invalid */
88
+ date?: string | Date;
86
89
  /** Last updated date as ISO string */
87
90
  updated?: string;
88
91
  /** Custom cache TTL in seconds (overrides global ISG settings) */
@@ -297,7 +300,7 @@ export interface PageModel {
297
300
  frontMatter: FrontMatter;
298
301
  /** Raw markdown content (before rendering) */
299
302
  content: string;
300
- /** Publication date (parsed from front matter or file stats) */
303
+ /** Publication date, from the first valid of `publishedAt`, `published`, `date`, `createdAt` */
301
304
  publishedAt?: Date;
302
305
  }
303
306
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/types/content.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,QAAQ;IACvB,qDAAqD;IACrD,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qCAAqC;IACrC,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,yCAAyC;IACzC,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,mCAAmC;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,+BAA+B;IAC/B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,iCAAiC;IACjC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;CACxF;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IACvF,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;IAChD,8CAA8C;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IAChC,qBAAqB;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gBAAgB;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,IAAI,CAAC,EAAE,SAAS,GAAG,qBAAqB,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC5D,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,6DAA6D;IAC7D,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IACxC,0BAA0B;IAC1B,QAAQ,EAAE;QACR,0CAA0C;QAC1C,UAAU,EAAE,MAAM,CAAC;QACnB,yCAAyC;QACzC,WAAW,EAAE,OAAO,CAAC;QACrB,6BAA6B;QAC7B,cAAc,EAAE,MAAM,CAAC;QACvB,iDAAiD;QACjD,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,IAAI,EAAE,OAAO,aAAa,EAAE,UAAU,CAAC;IACvC,0DAA0D;IAC1D,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,OAAO,iBAAiB,EAAE,OAAO,CAAC;QAC5C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,GAAG,EAAE;QACH,+BAA+B;QAC/B,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,EAAE,CAAC;QAC1C,oCAAoC;QACpC,OAAO,EAAE,MAAM,OAAO,iBAAiB,EAAE,OAAO,EAAE,CAAC;QACnD,6CAA6C;QAC7C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,iBAAiB,EAAE,OAAO,GAAG,SAAS,CAAC;QAC1E,6CAA6C;QAC7C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,iBAAiB,EAAE,OAAO,EAAE,CAAC;QACnE,2CAA2C;QAC3C,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,iBAAiB,EAAE,OAAO,GAAG,SAAS,CAAC;QAC5E,6CAA6C;QAC7C,WAAW,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,KAAK,OAAO,iBAAiB,EAAE,OAAO,EAAE,CAAC;QAC3F,mDAAmD;QACnD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,iBAAiB,EAAE,OAAO,EAAE,CAAC;QAClE,2CAA2C;QAC3C,cAAc,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,iBAAiB,EAAE,OAAO,EAAE,CAAC;QACvE,8CAA8C;QAC9C,cAAc,EAAE,MAAM,OAAO,iBAAiB,EAAE,OAAO,GAAG,SAAS,CAAC;KACrE,CAAC;IACF,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2FAA2F;IAC3F,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,0FAA0F;IAC1F,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/types/content.ts"],"names":[],"mappings":"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"}
@@ -144,7 +144,7 @@ export interface RSSFeedConfig {
144
144
  * Can be:
145
145
  * - A string property name from frontMatter (e.g., 'publishedAt', 'date')
146
146
  * - A function that returns a Date or string
147
- * @default 'publishedAt' or 'date'
147
+ * @default The first valid date in `publishedAt`, `published`, `date`, `createdAt`
148
148
  */
149
149
  pubDate?: string | ((page: PageModel) => Date | string);
150
150
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stati/core",
3
- "version": "1.27.0",
3
+ "version": "1.27.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",