@stati/core 1.3.0 → 1.3.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.
- package/dist/config/loader.d.ts +7 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +17 -12
- package/dist/constants.d.ts +71 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +78 -0
- package/dist/core/build.d.ts +1 -1
- package/dist/core/build.d.ts.map +1 -1
- package/dist/core/build.js +94 -69
- package/dist/core/content.d.ts +1 -1
- package/dist/core/content.d.ts.map +1 -1
- package/dist/core/content.js +10 -5
- package/dist/core/dev.d.ts +1 -7
- package/dist/core/dev.d.ts.map +1 -1
- package/dist/core/dev.js +135 -126
- package/dist/core/invalidate.d.ts +1 -1
- package/dist/core/invalidate.d.ts.map +1 -1
- package/dist/core/invalidate.js +3 -3
- package/dist/core/isg/build-lock.d.ts.map +1 -1
- package/dist/core/isg/build-lock.js +4 -2
- package/dist/core/isg/builder.d.ts +1 -1
- package/dist/core/isg/builder.d.ts.map +1 -1
- package/dist/core/isg/deps.d.ts +1 -1
- package/dist/core/isg/deps.d.ts.map +1 -1
- package/dist/core/isg/deps.js +59 -78
- package/dist/core/isg/hash.d.ts.map +1 -1
- package/dist/core/isg/hash.js +26 -17
- package/dist/core/isg/manifest.d.ts +1 -1
- package/dist/core/isg/manifest.d.ts.map +1 -1
- package/dist/core/isg/manifest.js +21 -8
- package/dist/core/isg/ttl.d.ts +1 -1
- package/dist/core/isg/ttl.d.ts.map +1 -1
- package/dist/core/isg/ttl.js +6 -9
- package/dist/core/isg/validation.d.ts +1 -1
- package/dist/core/isg/validation.d.ts.map +1 -1
- package/dist/core/markdown.d.ts +1 -1
- package/dist/core/markdown.d.ts.map +1 -1
- package/dist/core/navigation.d.ts +1 -1
- package/dist/core/navigation.d.ts.map +1 -1
- package/dist/core/templates.d.ts +1 -1
- package/dist/core/templates.d.ts.map +1 -1
- package/dist/core/templates.js +25 -103
- package/dist/core/utils/fs.d.ts +37 -0
- package/dist/core/utils/fs.d.ts.map +1 -0
- package/dist/core/utils/fs.js +86 -0
- package/dist/core/utils/partials.d.ts +24 -0
- package/dist/core/utils/partials.d.ts.map +1 -0
- package/dist/core/utils/partials.js +85 -0
- package/dist/core/utils/paths.d.ts +67 -0
- package/dist/core/utils/paths.d.ts.map +1 -0
- package/dist/core/utils/paths.js +86 -0
- package/dist/core/utils/template-discovery.d.ts +34 -0
- package/dist/core/utils/template-discovery.d.ts.map +1 -0
- package/dist/core/utils/template-discovery.js +111 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/tests/utils/test-mocks.d.ts +69 -0
- package/dist/tests/utils/test-mocks.d.ts.map +1 -0
- package/dist/tests/utils/test-mocks.js +125 -0
- package/dist/types/config.d.ts +178 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +1 -0
- package/dist/types/content.d.ts +124 -0
- package/dist/types/content.d.ts.map +1 -0
- package/dist/types/content.js +4 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/isg.d.ts +103 -0
- package/dist/types/isg.d.ts.map +1 -0
- package/dist/types/isg.js +4 -0
- package/dist/types/logging.d.ts +113 -0
- package/dist/types/logging.d.ts.map +1 -0
- package/dist/types/logging.js +4 -0
- package/dist/types/navigation.d.ts +43 -0
- package/dist/types/navigation.d.ts.map +1 -0
- package/dist/types/navigation.js +4 -0
- package/dist/types.d.ts +10 -10
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// dedup: partials scanning centralized in utils/partials.ts
|
|
2
|
+
import { join, dirname, relative, basename, posix } from 'path';
|
|
3
|
+
import glob from 'fast-glob';
|
|
4
|
+
import { resolveSrcDir } from './paths.js';
|
|
5
|
+
/**
|
|
6
|
+
* Scan underscore folders from root to the page's directory for .eta partials.
|
|
7
|
+
* Returns structured entries usable by both renderer and ISG deps.
|
|
8
|
+
*/
|
|
9
|
+
export async function scanPartialsHierarchy(pageRelativePath, config) {
|
|
10
|
+
const srcDir = resolveSrcDir(config);
|
|
11
|
+
const entries = [];
|
|
12
|
+
const dirsToScan = buildDirsToScan(pageRelativePath);
|
|
13
|
+
for (const dir of dirsToScan) {
|
|
14
|
+
const searchDir = dir ? join(srcDir, dir) : srcDir;
|
|
15
|
+
const normalizedSearchDir = searchDir.replace(/\\/g, '/');
|
|
16
|
+
const pattern = posix.join(normalizedSearchDir, '_*/**/*.eta');
|
|
17
|
+
try {
|
|
18
|
+
const files = (await glob(pattern, { absolute: true }));
|
|
19
|
+
for (const file of files) {
|
|
20
|
+
entries.push({
|
|
21
|
+
name: basename(file, '.eta'),
|
|
22
|
+
absPath: file,
|
|
23
|
+
relFromSrc: relative(srcDir, file),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Ignore errors for non-existent directories, continue scanning
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return entries;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the list of directories to scan for partials from root → current page directory.
|
|
36
|
+
*/
|
|
37
|
+
export function buildDirsToScan(pageRelativePath) {
|
|
38
|
+
const pageDir = dirname(pageRelativePath);
|
|
39
|
+
const pathSegments = pageDir === '.' ? [] : pageDir.split(/[/\\]/);
|
|
40
|
+
const dirsToScan = [''];
|
|
41
|
+
for (let i = 0; i < pathSegments.length; i++) {
|
|
42
|
+
dirsToScan.push(pathSegments.slice(0, i + 1).join('/'));
|
|
43
|
+
}
|
|
44
|
+
return dirsToScan;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Renderer-focused partial discovery.
|
|
48
|
+
* Matches historical renderer behavior used by templates tests:
|
|
49
|
+
* - Only scans the current page directory (not parents)
|
|
50
|
+
* - First finds underscore folders (e.g. "_partials/")
|
|
51
|
+
* - Then finds .eta files within those folders, returning relative paths like "_partials/header.eta"
|
|
52
|
+
*/
|
|
53
|
+
export async function scanPartialsForRenderer(pageRelativePath, config) {
|
|
54
|
+
const srcDir = resolveSrcDir(config);
|
|
55
|
+
const pageDirRel = dirname(pageRelativePath);
|
|
56
|
+
const searchDir = pageDirRel === '.' ? srcDir : join(srcDir, pageDirRel);
|
|
57
|
+
const entries = [];
|
|
58
|
+
try {
|
|
59
|
+
// Find underscore directories relative to the page directory
|
|
60
|
+
const underscoreDirs = (await glob('_*/', { cwd: searchDir }));
|
|
61
|
+
for (const dir of underscoreDirs) {
|
|
62
|
+
try {
|
|
63
|
+
// Find .eta files inside each underscore directory
|
|
64
|
+
const etaFiles = (await glob('*.eta', { cwd: join(searchDir, dir) }));
|
|
65
|
+
for (const file of etaFiles) {
|
|
66
|
+
const rel = join(dir, file);
|
|
67
|
+
entries.push({
|
|
68
|
+
name: basename(file, '.eta'),
|
|
69
|
+
absPath: join(searchDir, rel),
|
|
70
|
+
relFromSrc: rel,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Ignore per-dir errors and continue
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Ignore errors and return empty list (graceful)
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
return entries;
|
|
85
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { StatiConfig } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* File system path resolution utilities for Stati core.
|
|
4
|
+
* Centralized utilities to ensure consistent path handling across the codebase.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the absolute source directory path from configuration.
|
|
8
|
+
* @param config - Stati configuration
|
|
9
|
+
* @returns Absolute path to the source directory
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveSrcDir(config: StatiConfig): string;
|
|
12
|
+
/**
|
|
13
|
+
* Resolves the absolute output directory path from configuration.
|
|
14
|
+
* @param config - Stati configuration
|
|
15
|
+
* @returns Absolute path to the output directory
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveOutDir(config: StatiConfig): string;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the absolute static directory path from configuration.
|
|
20
|
+
* @param config - Stati configuration
|
|
21
|
+
* @returns Absolute path to the static directory
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveStaticDir(config: StatiConfig): string;
|
|
24
|
+
/**
|
|
25
|
+
* Resolves the absolute cache directory path.
|
|
26
|
+
* @returns Absolute path to the .stati cache directory
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCacheDir(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Resolves paths with defaults for development mode.
|
|
31
|
+
* @param config - Stati configuration
|
|
32
|
+
* @returns Object with all resolved directory paths with sensible defaults
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveDevPaths(config: StatiConfig): {
|
|
35
|
+
srcDir: string;
|
|
36
|
+
outDir: string;
|
|
37
|
+
staticDir: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Normalizes a template path for cross-platform compatibility.
|
|
41
|
+
* Ensures forward slashes are used for template engine compatibility.
|
|
42
|
+
* @param templatePath - Path to normalize
|
|
43
|
+
* @returns Normalized path with forward slashes
|
|
44
|
+
*/
|
|
45
|
+
export declare function normalizeTemplatePath(templatePath: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Resolves a file path relative to the source directory.
|
|
48
|
+
* @param config - Stati configuration
|
|
49
|
+
* @param relativePath - Path relative to srcDir
|
|
50
|
+
* @returns Absolute path
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveSrcPath(config: StatiConfig, relativePath: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves a file path relative to the output directory.
|
|
55
|
+
* @param config - Stati configuration
|
|
56
|
+
* @param relativePath - Path relative to outDir
|
|
57
|
+
* @returns Absolute path
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveOutPath(config: StatiConfig, relativePath: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Resolves a file path relative to the static directory.
|
|
62
|
+
* @param config - Stati configuration
|
|
63
|
+
* @param relativePath - Path relative to staticDir
|
|
64
|
+
* @returns Absolute path
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveStaticPath(config: StatiConfig, relativePath: string): string;
|
|
67
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/core/utils/paths.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQxD;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW;;;;EAMlD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { posix } from 'path';
|
|
3
|
+
import { DEFAULT_SRC_DIR, DEFAULT_OUT_DIR, DEFAULT_STATIC_DIR, CACHE_DIR_NAME, } from '../../constants.js';
|
|
4
|
+
/**
|
|
5
|
+
* File system path resolution utilities for Stati core.
|
|
6
|
+
* Centralized utilities to ensure consistent path handling across the codebase.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the absolute source directory path from configuration.
|
|
10
|
+
* @param config - Stati configuration
|
|
11
|
+
* @returns Absolute path to the source directory
|
|
12
|
+
*/
|
|
13
|
+
export function resolveSrcDir(config) {
|
|
14
|
+
return join(process.cwd(), config.srcDir);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolves the absolute output directory path from configuration.
|
|
18
|
+
* @param config - Stati configuration
|
|
19
|
+
* @returns Absolute path to the output directory
|
|
20
|
+
*/
|
|
21
|
+
export function resolveOutDir(config) {
|
|
22
|
+
return join(process.cwd(), config.outDir);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolves the absolute static directory path from configuration.
|
|
26
|
+
* @param config - Stati configuration
|
|
27
|
+
* @returns Absolute path to the static directory
|
|
28
|
+
*/
|
|
29
|
+
export function resolveStaticDir(config) {
|
|
30
|
+
return join(process.cwd(), config.staticDir);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolves the absolute cache directory path.
|
|
34
|
+
* @returns Absolute path to the .stati cache directory
|
|
35
|
+
*/
|
|
36
|
+
export function resolveCacheDir() {
|
|
37
|
+
return join(process.cwd(), CACHE_DIR_NAME);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolves paths with defaults for development mode.
|
|
41
|
+
* @param config - Stati configuration
|
|
42
|
+
* @returns Object with all resolved directory paths with sensible defaults
|
|
43
|
+
*/
|
|
44
|
+
export function resolveDevPaths(config) {
|
|
45
|
+
return {
|
|
46
|
+
srcDir: join(process.cwd(), config.srcDir || DEFAULT_SRC_DIR),
|
|
47
|
+
outDir: join(process.cwd(), config.outDir || DEFAULT_OUT_DIR),
|
|
48
|
+
staticDir: join(process.cwd(), config.staticDir || DEFAULT_STATIC_DIR),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Normalizes a template path for cross-platform compatibility.
|
|
53
|
+
* Ensures forward slashes are used for template engine compatibility.
|
|
54
|
+
* @param templatePath - Path to normalize
|
|
55
|
+
* @returns Normalized path with forward slashes
|
|
56
|
+
*/
|
|
57
|
+
export function normalizeTemplatePath(templatePath) {
|
|
58
|
+
return posix.normalize(templatePath.replace(/\\/g, '/'));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolves a file path relative to the source directory.
|
|
62
|
+
* @param config - Stati configuration
|
|
63
|
+
* @param relativePath - Path relative to srcDir
|
|
64
|
+
* @returns Absolute path
|
|
65
|
+
*/
|
|
66
|
+
export function resolveSrcPath(config, relativePath) {
|
|
67
|
+
return join(resolveSrcDir(config), relativePath);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolves a file path relative to the output directory.
|
|
71
|
+
* @param config - Stati configuration
|
|
72
|
+
* @param relativePath - Path relative to outDir
|
|
73
|
+
* @returns Absolute path
|
|
74
|
+
*/
|
|
75
|
+
export function resolveOutPath(config, relativePath) {
|
|
76
|
+
return join(resolveOutDir(config), relativePath);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Resolves a file path relative to the static directory.
|
|
80
|
+
* @param config - Stati configuration
|
|
81
|
+
* @param relativePath - Path relative to staticDir
|
|
82
|
+
* @returns Absolute path
|
|
83
|
+
*/
|
|
84
|
+
export function resolveStaticPath(config, relativePath) {
|
|
85
|
+
return join(resolveStaticDir(config), relativePath);
|
|
86
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { StatiConfig, PageModel } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared template discovery utilities.
|
|
4
|
+
* Extracted from templates.ts and isg/deps.ts to eliminate duplication.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Determines if the given page is a collection index page.
|
|
8
|
+
* A collection index page is one whose URL matches a directory path that contains other pages.
|
|
9
|
+
*
|
|
10
|
+
* @param page - The page to check
|
|
11
|
+
* @param allPages - All pages in the site (optional for simplified check)
|
|
12
|
+
* @returns True if the page is a collection index page
|
|
13
|
+
*/
|
|
14
|
+
export declare function isCollectionIndexPage(page: PageModel, allPages?: PageModel[]): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Discovers layout files by searching up the directory hierarchy.
|
|
17
|
+
* Supports both explicit layout specification and automatic discovery.
|
|
18
|
+
*
|
|
19
|
+
* @param pagePath - The path to the page (relative to srcDir)
|
|
20
|
+
* @param config - Stati configuration
|
|
21
|
+
* @param explicitLayout - Explicit layout name from front-matter
|
|
22
|
+
* @param isIndexPage - Whether this is an aggregation/index page (enables index.eta lookup)
|
|
23
|
+
* @returns The layout file path (relative to srcDir) or null if none found
|
|
24
|
+
*/
|
|
25
|
+
export declare function discoverLayout(pagePath: string, config: StatiConfig, explicitLayout?: string, isIndexPage?: boolean): Promise<string | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Gets the collection path for a given page URL.
|
|
28
|
+
* For index pages, returns the page's URL. For child pages, returns the parent directory.
|
|
29
|
+
*
|
|
30
|
+
* @param pageUrl - The page URL
|
|
31
|
+
* @returns The collection path
|
|
32
|
+
*/
|
|
33
|
+
export declare function getCollectionPathForPage(pageUrl: string): string;
|
|
34
|
+
//# sourceMappingURL=template-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-discovery.d.ts","sourceRoot":"","sources":["../../../src/core/utils/template-discovery.ts"],"names":[],"mappings":"AAGA,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"}
|
|
@@ -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,7 +19,7 @@
|
|
|
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
25
|
export type { InvalidationResult } from './core/invalidate.js';
|
|
@@ -27,7 +27,7 @@ export { build } from './core/build.js';
|
|
|
27
27
|
export { createDevServer } from './core/dev.js';
|
|
28
28
|
export { loadConfig } from './config/loader.js';
|
|
29
29
|
export { invalidate } from './core/invalidate.js';
|
|
30
|
-
import type { StatiConfig } from './types.js';
|
|
30
|
+
import type { StatiConfig } from './types/index.js';
|
|
31
31
|
/**
|
|
32
32
|
* Helper function for defining Stati configuration with TypeScript IntelliSense.
|
|
33
33
|
* Provides type checking and autocompletion for configuration options.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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,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,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D"}
|
|
@@ -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;
|