@stati/core 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/config/loader.d.ts +7 -1
  2. package/dist/config/loader.d.ts.map +1 -1
  3. package/dist/config/loader.js +17 -12
  4. package/dist/constants.d.ts +71 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +78 -0
  7. package/dist/core/build.d.ts +1 -1
  8. package/dist/core/build.d.ts.map +1 -1
  9. package/dist/core/build.js +94 -69
  10. package/dist/core/content.d.ts +1 -1
  11. package/dist/core/content.d.ts.map +1 -1
  12. package/dist/core/content.js +10 -5
  13. package/dist/core/dev.d.ts +1 -7
  14. package/dist/core/dev.d.ts.map +1 -1
  15. package/dist/core/dev.js +202 -141
  16. package/dist/core/invalidate.d.ts +1 -1
  17. package/dist/core/invalidate.d.ts.map +1 -1
  18. package/dist/core/invalidate.js +3 -3
  19. package/dist/core/isg/build-lock.d.ts.map +1 -1
  20. package/dist/core/isg/build-lock.js +4 -2
  21. package/dist/core/isg/builder.d.ts +1 -1
  22. package/dist/core/isg/builder.d.ts.map +1 -1
  23. package/dist/core/isg/deps.d.ts +1 -1
  24. package/dist/core/isg/deps.d.ts.map +1 -1
  25. package/dist/core/isg/deps.js +59 -78
  26. package/dist/core/isg/hash.d.ts.map +1 -1
  27. package/dist/core/isg/hash.js +26 -17
  28. package/dist/core/isg/manifest.d.ts +1 -1
  29. package/dist/core/isg/manifest.d.ts.map +1 -1
  30. package/dist/core/isg/manifest.js +21 -8
  31. package/dist/core/isg/ttl.d.ts +1 -1
  32. package/dist/core/isg/ttl.d.ts.map +1 -1
  33. package/dist/core/isg/ttl.js +6 -9
  34. package/dist/core/isg/validation.d.ts +1 -1
  35. package/dist/core/isg/validation.d.ts.map +1 -1
  36. package/dist/core/markdown.d.ts +1 -1
  37. package/dist/core/markdown.d.ts.map +1 -1
  38. package/dist/core/navigation.d.ts +1 -1
  39. package/dist/core/navigation.d.ts.map +1 -1
  40. package/dist/core/preview.d.ts +19 -0
  41. package/dist/core/preview.d.ts.map +1 -0
  42. package/dist/core/preview.js +163 -0
  43. package/dist/core/templates.d.ts +1 -1
  44. package/dist/core/templates.d.ts.map +1 -1
  45. package/dist/core/templates.js +28 -105
  46. package/dist/core/utils/fs.d.ts +37 -0
  47. package/dist/core/utils/fs.d.ts.map +1 -0
  48. package/dist/core/utils/fs.js +86 -0
  49. package/dist/core/utils/partials.d.ts +24 -0
  50. package/dist/core/utils/partials.d.ts.map +1 -0
  51. package/dist/core/utils/partials.js +85 -0
  52. package/dist/core/utils/paths.d.ts +67 -0
  53. package/dist/core/utils/paths.d.ts.map +1 -0
  54. package/dist/core/utils/paths.js +86 -0
  55. package/dist/core/utils/template-discovery.d.ts +34 -0
  56. package/dist/core/utils/template-discovery.d.ts.map +1 -0
  57. package/dist/core/utils/template-discovery.js +111 -0
  58. package/dist/index.d.ts +4 -2
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +1 -0
  61. package/dist/tests/utils/test-mocks.d.ts +69 -0
  62. package/dist/tests/utils/test-mocks.d.ts.map +1 -0
  63. package/dist/tests/utils/test-mocks.js +125 -0
  64. package/dist/types/config.d.ts +178 -0
  65. package/dist/types/config.d.ts.map +1 -0
  66. package/dist/types/config.js +1 -0
  67. package/dist/types/content.d.ts +124 -0
  68. package/dist/types/content.d.ts.map +1 -0
  69. package/dist/types/content.js +4 -0
  70. package/dist/types/index.d.ts +10 -0
  71. package/dist/types/index.d.ts.map +1 -0
  72. package/dist/types/index.js +5 -0
  73. package/dist/types/isg.d.ts +103 -0
  74. package/dist/types/isg.d.ts.map +1 -0
  75. package/dist/types/isg.js +4 -0
  76. package/dist/types/logging.d.ts +113 -0
  77. package/dist/types/logging.d.ts.map +1 -0
  78. package/dist/types/logging.js +4 -0
  79. package/dist/types/navigation.d.ts +43 -0
  80. package/dist/types/navigation.d.ts.map +1 -0
  81. package/dist/types/navigation.js +4 -0
  82. package/dist/types.d.ts +10 -10
  83. package/dist/types.d.ts.map +1 -1
  84. package/package.json +1 -1
@@ -0,0 +1,111 @@
1
+ import { join, dirname } from 'path';
2
+ import { posix } from 'path';
3
+ import { pathExists } from './fs.js';
4
+ import { LAYOUT_TEMPLATE } from '../../constants.js';
5
+ import { resolveSrcDir } from './paths.js';
6
+ /**
7
+ * Shared template discovery utilities.
8
+ * Extracted from templates.ts and isg/deps.ts to eliminate duplication.
9
+ */
10
+ /**
11
+ * Determines if the given page is a collection index page.
12
+ * A collection index page is one whose URL matches a directory path that contains other pages.
13
+ *
14
+ * @param page - The page to check
15
+ * @param allPages - All pages in the site (optional for simplified check)
16
+ * @returns True if the page is a collection index page
17
+ */
18
+ export function isCollectionIndexPage(page, allPages) {
19
+ // Root index page is always a collection index
20
+ if (page.url === '/') {
21
+ return true;
22
+ }
23
+ // If we have all pages, do a proper check
24
+ if (allPages) {
25
+ // Check if this page's URL is a directory path that contains other pages
26
+ const pageUrlAsDir = page.url.endsWith('/') ? page.url : page.url + '/';
27
+ return allPages.some((otherPage) => otherPage.url !== page.url && otherPage.url.startsWith(pageUrlAsDir));
28
+ }
29
+ // Simplified version when we don't have all pages
30
+ // Assume any page ending in /index or at root is a collection page
31
+ return page.url.endsWith('/index') || page.slug === 'index';
32
+ }
33
+ /**
34
+ * Discovers layout files by searching up the directory hierarchy.
35
+ * Supports both explicit layout specification and automatic discovery.
36
+ *
37
+ * @param pagePath - The path to the page (relative to srcDir)
38
+ * @param config - Stati configuration
39
+ * @param explicitLayout - Explicit layout name from front-matter
40
+ * @param isIndexPage - Whether this is an aggregation/index page (enables index.eta lookup)
41
+ * @returns The layout file path (relative to srcDir) or null if none found
42
+ */
43
+ export async function discoverLayout(pagePath, config, explicitLayout, isIndexPage) {
44
+ // Early return if required config values are missing
45
+ if (!config.srcDir) {
46
+ return null;
47
+ }
48
+ const srcDir = resolveSrcDir(config);
49
+ // If explicit layout is specified, use it
50
+ if (explicitLayout) {
51
+ const layoutPath = join(srcDir, `${explicitLayout}.eta`);
52
+ if (await pathExists(layoutPath)) {
53
+ return `${explicitLayout}.eta`;
54
+ }
55
+ }
56
+ // Get the directory of the current page
57
+ const pageDir = dirname(pagePath);
58
+ const pathSegments = pageDir === '.' ? [] : pageDir.split(/[/\\]/); // Handle both separators
59
+ // Search for layout.eta from current directory up to root
60
+ const dirsToSearch = [];
61
+ // Add current directory if not root
62
+ if (pathSegments.length > 0) {
63
+ for (let i = pathSegments.length; i > 0; i--) {
64
+ dirsToSearch.push(pathSegments.slice(0, i).join('/'));
65
+ }
66
+ }
67
+ // Add root directory
68
+ dirsToSearch.push('');
69
+ for (const dir of dirsToSearch) {
70
+ // For index pages, first check for index.eta in each directory
71
+ if (isIndexPage) {
72
+ const indexLayoutPath = dir ? join(srcDir, dir, 'index.eta') : join(srcDir, 'index.eta');
73
+ if (await pathExists(indexLayoutPath)) {
74
+ // Return relative path with forward slashes for Eta
75
+ const relativePath = dir ? `${dir}/index.eta` : 'index.eta';
76
+ return posix.normalize(relativePath);
77
+ }
78
+ }
79
+ // Then check for layout.eta as fallback
80
+ const layoutPath = dir ? join(srcDir, dir, LAYOUT_TEMPLATE) : join(srcDir, LAYOUT_TEMPLATE);
81
+ if (await pathExists(layoutPath)) {
82
+ // Return relative path with forward slashes for Eta
83
+ const relativePath = dir ? `${dir}/${LAYOUT_TEMPLATE}` : LAYOUT_TEMPLATE;
84
+ return posix.normalize(relativePath);
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+ /**
90
+ * Gets the collection path for a given page URL.
91
+ * For index pages, returns the page's URL. For child pages, returns the parent directory.
92
+ *
93
+ * @param pageUrl - The page URL
94
+ * @returns The collection path
95
+ */
96
+ export function getCollectionPathForPage(pageUrl) {
97
+ if (pageUrl === '/') {
98
+ return '/';
99
+ }
100
+ // If it's an index page (ends with /), return the URL as-is
101
+ if (pageUrl.endsWith('/')) {
102
+ return pageUrl;
103
+ }
104
+ // For non-index pages, return the parent directory
105
+ const lastSlashIndex = pageUrl.lastIndexOf('/');
106
+ if (lastSlashIndex === -1) {
107
+ return '/';
108
+ }
109
+ const parentPath = pageUrl.substring(0, lastSlashIndex + 1);
110
+ return parentPath === '' ? '/' : parentPath;
111
+ }
package/dist/index.d.ts CHANGED
@@ -19,15 +19,17 @@
19
19
  * await build({ clean: true });
20
20
  * ```
21
21
  */
22
- export type { StatiConfig, PageModel, FrontMatter, BuildContext, PageContext, BuildHooks, NavNode, ISGConfig, AgingRule, BuildStats, } from './types.js';
22
+ export type { StatiConfig, PageModel, FrontMatter, BuildContext, PageContext, BuildHooks, NavNode, ISGConfig, AgingRule, BuildStats, } from './types/index.js';
23
23
  export type { BuildOptions } from './core/build.js';
24
24
  export type { DevServerOptions } from './core/dev.js';
25
+ export type { PreviewServerOptions } from './core/preview.js';
25
26
  export type { InvalidationResult } from './core/invalidate.js';
26
27
  export { build } from './core/build.js';
27
28
  export { createDevServer } from './core/dev.js';
29
+ export { createPreviewServer } from './core/preview.js';
28
30
  export { loadConfig } from './config/loader.js';
29
31
  export { invalidate } from './core/invalidate.js';
30
- import type { StatiConfig } from './types.js';
32
+ import type { StatiConfig } from './types/index.js';
31
33
  /**
32
34
  * Helper function for defining Stati configuration with TypeScript IntelliSense.
33
35
  * Provides type checking and autocompletion for configuration options.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,WAAW,EACX,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D"}
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@
21
21
  */
22
22
  export { build } from './core/build.js';
23
23
  export { createDevServer } from './core/dev.js';
24
+ export { createPreviewServer } from './core/preview.js';
24
25
  export { loadConfig } from './config/loader.js';
25
26
  export { invalidate } from './core/invalidate.js';
26
27
  /**
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Test mock utilities for Stati core tests.
3
+ * Centralized mock factory functions to reduce duplication across test files.
4
+ */
5
+ /**
6
+ * Creates a complete set of hoisted mocks for build tests.
7
+ * Use this inside vi.hoisted() calls.
8
+ */
9
+ export declare function createBuildTestMocksObject(): {
10
+ mockEnsureDir: import("vitest").Mock<(...args: any[]) => any>;
11
+ mockWriteFile: import("vitest").Mock<(...args: any[]) => any>;
12
+ mockCopy: import("vitest").Mock<(...args: any[]) => any>;
13
+ mockCopyFile: import("vitest").Mock<(...args: any[]) => any>;
14
+ mockRemove: import("vitest").Mock<(...args: any[]) => any>;
15
+ mockPathExists: import("vitest").Mock<(...args: any[]) => any>;
16
+ mockReaddir: import("vitest").Mock<(...args: any[]) => any>;
17
+ mockStat: import("vitest").Mock<(...args: any[]) => any>;
18
+ mockReadFile: import("vitest").Mock<(...args: any[]) => any>;
19
+ mockLoadConfig: import("vitest").Mock<(...args: any[]) => any>;
20
+ mockLoadContent: import("vitest").Mock<(...args: any[]) => any>;
21
+ mockCreateMarkdownProcessor: import("vitest").Mock<(...args: any[]) => any>;
22
+ mockRenderMarkdown: import("vitest").Mock<(...args: any[]) => any>;
23
+ mockCreateTemplateEngine: import("vitest").Mock<(...args: any[]) => any>;
24
+ mockRenderPage: import("vitest").Mock<(...args: any[]) => any>;
25
+ mockBuildNavigation: import("vitest").Mock<(...args: any[]) => any>;
26
+ mockLoadCacheManifest: import("vitest").Mock<(...args: any[]) => any>;
27
+ mockSaveCacheManifest: import("vitest").Mock<(...args: any[]) => any>;
28
+ mockShouldRebuildPage: import("vitest").Mock<(...args: any[]) => any>;
29
+ mockCreateCacheEntry: import("vitest").Mock<(...args: any[]) => any>;
30
+ mockUpdateCacheEntry: import("vitest").Mock<(...args: any[]) => any>;
31
+ mockWithBuildLock: import("vitest").Mock<(...args: any[]) => any>;
32
+ mockComputeContentHash: import("vitest").Mock<(...args: any[]) => any>;
33
+ mockComputeFileHash: import("vitest").Mock<(...args: any[]) => any>;
34
+ mockComputeInputsHash: import("vitest").Mock<(...args: any[]) => any>;
35
+ mockTrackTemplateDependencies: import("vitest").Mock<(...args: any[]) => any>;
36
+ mockComputeEffectiveTTL: import("vitest").Mock<(...args: any[]) => any>;
37
+ mockComputeNextRebuildAt: import("vitest").Mock<(...args: any[]) => any>;
38
+ mockIsPageFrozen: import("vitest").Mock<(...args: any[]) => any>;
39
+ };
40
+ /**
41
+ * Creates fs-extra module mock implementation.
42
+ */
43
+ export declare function mockFsExtraModule(mocks: ReturnType<typeof createBuildTestMocksObject>): {
44
+ default: {
45
+ ensureDir: import("vitest").Mock<(...args: any[]) => any>;
46
+ writeFile: import("vitest").Mock<(...args: any[]) => any>;
47
+ copy: import("vitest").Mock<(...args: any[]) => any>;
48
+ copyFile: import("vitest").Mock<(...args: any[]) => any>;
49
+ remove: import("vitest").Mock<(...args: any[]) => any>;
50
+ pathExists: import("vitest").Mock<(...args: any[]) => any>;
51
+ readdir: import("vitest").Mock<(...args: any[]) => any>;
52
+ stat: import("vitest").Mock<(...args: any[]) => any>;
53
+ readFile: import("vitest").Mock<(...args: any[]) => any>;
54
+ };
55
+ };
56
+ /**
57
+ * Sets up standard module mocks for build tests.
58
+ * Call this after creating mocks with vi.hoisted().
59
+ */
60
+ export declare function setupBuildModuleMocks(mocks: ReturnType<typeof createBuildTestMocksObject>): void;
61
+ /**
62
+ * Sets up ISG-specific module mocks.
63
+ */
64
+ export declare function setupISGModuleMocks(mocks: ReturnType<typeof createBuildTestMocksObject>): void;
65
+ export declare const createFsExtraMocks: typeof createBuildTestMocksObject;
66
+ export declare const createCoreModuleMocks: typeof createBuildTestMocksObject;
67
+ export declare const createISGMocks: typeof createBuildTestMocksObject;
68
+ export declare const createBuildTestMocks: typeof createBuildTestMocksObject;
69
+ //# sourceMappingURL=test-mocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-mocks.d.ts","sourceRoot":"","sources":["../../../src/tests/utils/test-mocks.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCzC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC;;;;;;;;;;;;EAcrF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,QAuCzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,QAgBvF;AAGD,eAAO,MAAM,kBAAkB,mCAA6B,CAAC;AAC7D,eAAO,MAAM,qBAAqB,mCAA6B,CAAC;AAChE,eAAO,MAAM,cAAc,mCAA6B,CAAC;AACzD,eAAO,MAAM,oBAAoB,mCAA6B,CAAC"}
@@ -0,0 +1,125 @@
1
+ import { vi } from 'vitest';
2
+ /**
3
+ * Test mock utilities for Stati core tests.
4
+ * Centralized mock factory functions to reduce duplication across test files.
5
+ */
6
+ /**
7
+ * Creates a complete set of hoisted mocks for build tests.
8
+ * Use this inside vi.hoisted() calls.
9
+ */
10
+ export function createBuildTestMocksObject() {
11
+ return {
12
+ // fs-extra mocks
13
+ mockEnsureDir: vi.fn(),
14
+ mockWriteFile: vi.fn(),
15
+ mockCopy: vi.fn(),
16
+ mockCopyFile: vi.fn(),
17
+ mockRemove: vi.fn(),
18
+ mockPathExists: vi.fn(),
19
+ mockReaddir: vi.fn(),
20
+ mockStat: vi.fn(),
21
+ mockReadFile: vi.fn(),
22
+ // core module mocks
23
+ mockLoadConfig: vi.fn(),
24
+ mockLoadContent: vi.fn(),
25
+ mockCreateMarkdownProcessor: vi.fn(),
26
+ mockRenderMarkdown: vi.fn(),
27
+ mockCreateTemplateEngine: vi.fn(),
28
+ mockRenderPage: vi.fn(),
29
+ mockBuildNavigation: vi.fn(),
30
+ // ISG mocks
31
+ mockLoadCacheManifest: vi.fn(),
32
+ mockSaveCacheManifest: vi.fn(),
33
+ mockShouldRebuildPage: vi.fn(),
34
+ mockCreateCacheEntry: vi.fn(),
35
+ mockUpdateCacheEntry: vi.fn(),
36
+ mockWithBuildLock: vi.fn(),
37
+ // Hash mocks
38
+ mockComputeContentHash: vi.fn(),
39
+ mockComputeFileHash: vi.fn(),
40
+ mockComputeInputsHash: vi.fn(),
41
+ // Dependency tracking mocks
42
+ mockTrackTemplateDependencies: vi.fn(),
43
+ // TTL mocks
44
+ mockComputeEffectiveTTL: vi.fn(),
45
+ mockComputeNextRebuildAt: vi.fn(),
46
+ mockIsPageFrozen: vi.fn(),
47
+ };
48
+ }
49
+ /**
50
+ * Creates fs-extra module mock implementation.
51
+ */
52
+ export function mockFsExtraModule(mocks) {
53
+ return {
54
+ default: {
55
+ ensureDir: mocks.mockEnsureDir,
56
+ writeFile: mocks.mockWriteFile,
57
+ copy: mocks.mockCopy,
58
+ copyFile: mocks.mockCopyFile,
59
+ remove: mocks.mockRemove,
60
+ pathExists: mocks.mockPathExists,
61
+ readdir: mocks.mockReaddir,
62
+ stat: mocks.mockStat,
63
+ readFile: mocks.mockReadFile,
64
+ },
65
+ };
66
+ }
67
+ /**
68
+ * Sets up standard module mocks for build tests.
69
+ * Call this after creating mocks with vi.hoisted().
70
+ */
71
+ export function setupBuildModuleMocks(mocks) {
72
+ vi.mock('fs-extra', () => mockFsExtraModule(mocks));
73
+ vi.mock('../../config/loader.js', () => ({
74
+ loadConfig: mocks.mockLoadConfig,
75
+ }));
76
+ vi.mock('../../core/content.js', () => ({
77
+ loadContent: mocks.mockLoadContent,
78
+ }));
79
+ vi.mock('../../core/markdown.js', () => ({
80
+ createMarkdownProcessor: mocks.mockCreateMarkdownProcessor,
81
+ renderMarkdown: mocks.mockRenderMarkdown,
82
+ }));
83
+ vi.mock('../../core/templates.js', () => ({
84
+ createTemplateEngine: mocks.mockCreateTemplateEngine,
85
+ renderPage: mocks.mockRenderPage,
86
+ }));
87
+ vi.mock('../../core/navigation.js', () => ({
88
+ buildNavigation: mocks.mockBuildNavigation,
89
+ }));
90
+ vi.mock('../../core/isg/manifest.js', () => ({
91
+ loadCacheManifest: mocks.mockLoadCacheManifest,
92
+ saveCacheManifest: mocks.mockSaveCacheManifest,
93
+ }));
94
+ vi.mock('../../core/isg/builder.js', () => ({
95
+ shouldRebuildPage: mocks.mockShouldRebuildPage,
96
+ createCacheEntry: mocks.mockCreateCacheEntry,
97
+ updateCacheEntry: mocks.mockUpdateCacheEntry,
98
+ }));
99
+ vi.mock('../../core/isg/build-lock.js', () => ({
100
+ withBuildLock: mocks.mockWithBuildLock,
101
+ }));
102
+ }
103
+ /**
104
+ * Sets up ISG-specific module mocks.
105
+ */
106
+ export function setupISGModuleMocks(mocks) {
107
+ vi.mock('../../core/isg/hash.js', () => ({
108
+ computeContentHash: mocks.mockComputeContentHash,
109
+ computeFileHash: mocks.mockComputeFileHash,
110
+ computeInputsHash: mocks.mockComputeInputsHash,
111
+ }));
112
+ vi.mock('../../core/isg/deps.js', () => ({
113
+ trackTemplateDependencies: mocks.mockTrackTemplateDependencies,
114
+ }));
115
+ vi.mock('../../core/isg/ttl.js', () => ({
116
+ computeEffectiveTTL: mocks.mockComputeEffectiveTTL,
117
+ computeNextRebuildAt: mocks.mockComputeNextRebuildAt,
118
+ isPageFrozen: mocks.mockIsPageFrozen,
119
+ }));
120
+ }
121
+ // Legacy exports for compatibility
122
+ export const createFsExtraMocks = createBuildTestMocksObject;
123
+ export const createCoreModuleMocks = createBuildTestMocksObject;
124
+ export const createISGMocks = createBuildTestMocksObject;
125
+ export const createBuildTestMocks = createBuildTestMocksObject;
@@ -0,0 +1,178 @@
1
+ import type MarkdownIt from 'markdown-it';
2
+ /**
3
+ * Configuration related type definitions
4
+ */
5
+ /**
6
+ * Site-wide configuration settings.
7
+ * Contains global metadata and URL configuration for the static site.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const siteConfig: SiteConfig = {
12
+ * title: "My Awesome Blog",
13
+ * baseUrl: "https://myblog.com",
14
+ * defaultLocale: "en-US"
15
+ * };
16
+ * ```
17
+ */
18
+ export interface SiteConfig {
19
+ /** The site's title, used in templates and metadata */
20
+ readonly title: string;
21
+ /** Base URL for the site, used for absolute URL generation */
22
+ readonly baseUrl: string;
23
+ /** Default locale for internationalization (optional) */
24
+ readonly defaultLocale?: string;
25
+ }
26
+ /**
27
+ * Main configuration interface for Stati static site generator.
28
+ * Defines all options for site generation, including directories, templates, and features.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const config: StatiConfig = {
33
+ * srcDir: 'site',
34
+ * outDir: 'dist',
35
+ * staticDir: 'public',
36
+ * site: {
37
+ * title: 'My Blog',
38
+ * baseUrl: 'https://example.com'
39
+ * },
40
+ * markdown: {
41
+ * configure: (md) => md.use(somePlugin)
42
+ * }
43
+ * };
44
+ * ```
45
+ */
46
+ export interface StatiConfig {
47
+ /** Source directory for content files (default: 'site') */
48
+ srcDir?: string;
49
+ /** Output directory for generated site (default: 'dist') */
50
+ outDir?: string;
51
+ /** Directory for static assets (default: 'public') */
52
+ staticDir?: string;
53
+ /** Site-wide configuration */
54
+ site: SiteConfig;
55
+ /** Markdown processing configuration */
56
+ markdown?: {
57
+ /** Array of plugins to load - each item can be a string (plugin name) or [string, options] tuple */
58
+ plugins?: (string | [string, unknown])[];
59
+ /** Function to configure the MarkdownIt instance */
60
+ configure?: (md: MarkdownIt) => void;
61
+ };
62
+ /** Eta template engine configuration */
63
+ eta?: {
64
+ /** Custom template filters */
65
+ filters?: Record<string, (x: unknown) => unknown>;
66
+ };
67
+ /** Incremental Static Generation configuration */
68
+ isg?: import('./isg.js').ISGConfig;
69
+ /** Development server configuration */
70
+ dev?: {
71
+ /** Port for development server (default: 3000) */
72
+ port?: number;
73
+ /** Host for development server (default: 'localhost') */
74
+ host?: string;
75
+ /** Whether to open browser automatically (default: false) */
76
+ open?: boolean;
77
+ };
78
+ /** Build lifecycle hooks */
79
+ hooks?: BuildHooks;
80
+ }
81
+ /**
82
+ * Build context passed to build lifecycle hooks.
83
+ * Contains the full configuration and all loaded pages.
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const buildHook = async (ctx: BuildContext) => {
88
+ * console.log(`Building ${ctx.pages.length} pages`);
89
+ * console.log(`Output directory: ${ctx.config.outDir}`);
90
+ * };
91
+ * ```
92
+ */
93
+ export interface BuildContext {
94
+ /** The resolved Stati configuration */
95
+ config: StatiConfig;
96
+ /** Array of all loaded page models */
97
+ pages: import('./content.js').PageModel[];
98
+ }
99
+ /**
100
+ * Page context passed to page-specific lifecycle hooks.
101
+ * Contains the current page being processed and site configuration.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const pageHook = async (ctx: PageContext) => {
106
+ * console.log(`Rendering page: ${ctx.page.slug}`);
107
+ * // Modify page content or metadata
108
+ * ctx.page.frontMatter.customField = 'processed';
109
+ * };
110
+ * ```
111
+ */
112
+ export interface PageContext {
113
+ /** The page model being processed */
114
+ page: import('./content.js').PageModel;
115
+ /** The resolved Stati configuration */
116
+ config: StatiConfig;
117
+ }
118
+ /**
119
+ * Build lifecycle hooks for customizing the site generation process.
120
+ * Allows developers to inject custom logic at various stages of the build.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const hooks: BuildHooks = {
125
+ * beforeAll: async (ctx) => {
126
+ * console.log('Starting build...');
127
+ * },
128
+ * beforeRender: async (ctx) => {
129
+ * // Add custom data to page context
130
+ * ctx.page.frontMatter.buildTime = new Date().toISOString();
131
+ * },
132
+ * afterAll: async (ctx) => {
133
+ * console.log(`Build complete! Generated ${ctx.pages.length} pages.`);
134
+ * }
135
+ * };
136
+ * ```
137
+ */
138
+ export interface BuildHooks {
139
+ /** Called before starting the build process */
140
+ beforeAll?: (ctx: BuildContext) => Promise<void> | void;
141
+ /** Called after completing the build process */
142
+ afterAll?: (ctx: BuildContext) => Promise<void> | void;
143
+ /** Called before rendering each individual page */
144
+ beforeRender?: (ctx: PageContext) => Promise<void> | void;
145
+ /** Called after rendering each individual page */
146
+ afterRender?: (ctx: PageContext) => Promise<void> | void;
147
+ }
148
+ /**
149
+ * Statistics collected during the build process.
150
+ * Provides useful metrics about the site generation.
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * const stats: BuildStats = {
155
+ * totalPages: 15,
156
+ * assetsCount: 8,
157
+ * buildTimeMs: 1250,
158
+ * outputSizeBytes: 2048576,
159
+ * cacheHits: 5,
160
+ * cacheMisses: 10
161
+ * };
162
+ * ```
163
+ */
164
+ export interface BuildStats {
165
+ /** Total number of pages processed */
166
+ totalPages: number;
167
+ /** Number of static assets copied */
168
+ assetsCount: number;
169
+ /** Total build time in milliseconds */
170
+ buildTimeMs: number;
171
+ /** Total size of output directory in bytes */
172
+ outputSizeBytes: number;
173
+ /** Number of cache hits (if caching enabled) */
174
+ cacheHits?: number;
175
+ /** Number of cache misses (if caching enabled) */
176
+ cacheMisses?: number;
177
+ }
178
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Content related type definitions
3
+ */
4
+ /**
5
+ * Front matter metadata extracted from content files.
6
+ * Contains page-specific configuration and metadata in YAML format.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const frontMatter: FrontMatter = {
11
+ * title: 'Getting Started with Stati',
12
+ * description: 'A comprehensive guide to static site generation',
13
+ * tags: ['tutorial', 'documentation'],
14
+ * layout: 'post',
15
+ * order: 1,
16
+ * publishedAt: '2024-01-01',
17
+ * ttlSeconds: 3600,
18
+ * draft: false
19
+ * };
20
+ * ```
21
+ */
22
+ export interface FrontMatter {
23
+ /** Page title for SEO and display */
24
+ title?: string;
25
+ /** Page description for SEO and meta tags */
26
+ description?: string;
27
+ /** Array of tags for categorization */
28
+ tags?: readonly string[];
29
+ /** Template layout to use for rendering */
30
+ layout?: string;
31
+ /** Numeric order for sorting (useful for navigation) */
32
+ order?: number;
33
+ /** Publication date as ISO string */
34
+ publishedAt?: string;
35
+ /** Custom cache TTL in seconds (overrides global ISG settings) */
36
+ ttlSeconds?: number;
37
+ /** Custom max age cap in days (overrides global ISG settings) */
38
+ maxAgeCapDays?: number;
39
+ /** Whether the page is a draft (excludes from build) */
40
+ draft?: boolean;
41
+ /** Additional custom properties */
42
+ [key: string]: unknown;
43
+ }
44
+ /**
45
+ * Represents a single page in the static site.
46
+ * Contains all metadata, content, and URL information for a page.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const page: PageModel = {
51
+ * slug: 'my-first-post',
52
+ * url: '/blog/my-first-post',
53
+ * sourcePath: '/content/blog/my-first-post.md',
54
+ * frontMatter: {
55
+ * title: 'My First Post',
56
+ * tags: ['intro', 'blog']
57
+ * },
58
+ * content: '<p>Hello world!</p>',
59
+ * publishedAt: new Date('2024-01-01')
60
+ * };
61
+ * ```
62
+ */
63
+ export interface PageModel {
64
+ /** URL-friendly identifier for the page */
65
+ slug: string;
66
+ /** Full URL path for the page */
67
+ url: string;
68
+ /** Absolute path to the source content file */
69
+ sourcePath: string;
70
+ /** Parsed front matter metadata */
71
+ frontMatter: FrontMatter;
72
+ /** Rendered HTML content */
73
+ content: string;
74
+ /** Publication date (parsed from front matter or file stats) */
75
+ publishedAt?: Date;
76
+ }
77
+ /**
78
+ * Collection aggregation data available to index page templates.
79
+ * Provides access to child pages and collection metadata for content listing.
80
+ */
81
+ export interface CollectionData {
82
+ /** All pages in the current collection */
83
+ pages: PageModel[];
84
+ /** Direct child pages of the collection */
85
+ children: PageModel[];
86
+ /** Recent pages sorted by publishedAt (most recent first) */
87
+ recentPages: PageModel[];
88
+ /** Pages grouped by tags for aggregation */
89
+ pagesByTag: Record<string, PageModel[]>;
90
+ /** Collection metadata */
91
+ metadata: {
92
+ /** Total number of pages in collection */
93
+ totalPages: number;
94
+ /** Whether collection has child pages */
95
+ hasChildren: boolean;
96
+ /** Path of the collection */
97
+ collectionPath: string;
98
+ /** Name of the collection (derived from path) */
99
+ collectionName: string;
100
+ };
101
+ }
102
+ /**
103
+ * Template rendering context passed to Eta layouts.
104
+ * Contains all data available to templates during rendering.
105
+ */
106
+ export interface TemplateContext {
107
+ /** Site configuration and metadata */
108
+ site: import('./config.js').SiteConfig;
109
+ /** Current page data including frontmatter and content */
110
+ page: {
111
+ path: string;
112
+ content: string;
113
+ [key: string]: unknown;
114
+ };
115
+ /** Rendered markdown content */
116
+ content: string;
117
+ /** Site navigation tree */
118
+ navigation: import('./navigation.js').NavNode[];
119
+ /** Discovered partials from underscore folders in hierarchy */
120
+ partials: Record<string, string>;
121
+ /** Collection data for index pages (only available on collection index pages) */
122
+ collection?: CollectionData;
123
+ }
124
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/types/content.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;;;;;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,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mCAAmC;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;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,4BAA4B;IAC5B,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,OAAO,EAAE,MAAM,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,UAAU,EAAE,OAAO,iBAAiB,EAAE,OAAO,EAAE,CAAC;IAChD,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iFAAiF;IACjF,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Content related type definitions
3
+ */
4
+ export {};