@stati/core 1.28.0 → 1.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/core/build.d.ts.map +1 -1
- package/dist/core/build.js +192 -71
- package/dist/core/collections.d.ts +139 -0
- package/dist/core/collections.d.ts.map +1 -0
- package/dist/core/collections.js +342 -0
- package/dist/core/content.d.ts.map +1 -1
- package/dist/core/content.js +2 -1
- package/dist/core/dev.d.ts.map +1 -1
- package/dist/core/dev.js +32 -93
- package/dist/core/expand.d.ts +86 -0
- package/dist/core/expand.d.ts.map +1 -0
- package/dist/core/expand.js +484 -0
- package/dist/core/isg/builder.d.ts +15 -2
- package/dist/core/isg/builder.d.ts.map +1 -1
- package/dist/core/isg/builder.js +44 -6
- package/dist/core/isg/hash.d.ts +4 -2
- package/dist/core/isg/hash.d.ts.map +1 -1
- package/dist/core/isg/hash.js +16 -2
- package/dist/core/isg/index.d.ts +2 -1
- package/dist/core/isg/index.d.ts.map +1 -1
- package/dist/core/isg/index.js +3 -1
- package/dist/core/isg/manifest.js +6 -0
- package/dist/core/isg/prune.d.ts +32 -0
- package/dist/core/isg/prune.d.ts.map +1 -0
- package/dist/core/isg/prune.js +113 -0
- package/dist/core/navigation.d.ts +4 -2
- package/dist/core/navigation.d.ts.map +1 -1
- package/dist/core/navigation.js +17 -2
- package/dist/core/preview.d.ts.map +1 -1
- package/dist/core/preview.js +21 -32
- package/dist/core/templates.d.ts +3 -0
- package/dist/core/templates.d.ts.map +1 -1
- package/dist/core/templates.js +67 -127
- package/dist/core/utils/bundle-matching.utils.d.ts +8 -8
- package/dist/core/utils/bundle-matching.utils.d.ts.map +1 -1
- package/dist/core/utils/bundle-matching.utils.js +10 -10
- package/dist/core/utils/fs.utils.d.ts +8 -0
- package/dist/core/utils/fs.utils.d.ts.map +1 -1
- package/dist/core/utils/fs.utils.js +13 -1
- package/dist/core/utils/index.d.ts +6 -6
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core/utils/index.js +5 -5
- package/dist/core/utils/paths.utils.d.ts +15 -0
- package/dist/core/utils/paths.utils.d.ts.map +1 -1
- package/dist/core/utils/paths.utils.js +20 -0
- package/dist/core/utils/server.utils.d.ts +38 -3
- package/dist/core/utils/server.utils.d.ts.map +1 -1
- package/dist/core/utils/server.utils.js +132 -44
- package/dist/core/utils/template-discovery.utils.d.ts +25 -1
- package/dist/core/utils/template-discovery.utils.d.ts.map +1 -1
- package/dist/core/utils/template-discovery.utils.js +61 -11
- package/dist/core/utils/typescript.utils.d.ts +27 -0
- package/dist/core/utils/typescript.utils.d.ts.map +1 -1
- package/dist/core/utils/typescript.utils.js +34 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/rss/generator.d.ts.map +1 -1
- package/dist/rss/generator.js +5 -2
- package/dist/search/generator.d.ts +27 -7
- package/dist/search/generator.d.ts.map +1 -1
- package/dist/search/generator.js +55 -12
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +1 -1
- package/dist/types/config.d.ts +61 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/content.d.ts +129 -1
- package/dist/types/content.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/isg.d.ts +12 -0
- package/dist/types/isg.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -55,7 +55,7 @@ export interface CompiledBundleInfo {
|
|
|
55
55
|
* 3. If page matches any `exclude` pattern: bundle is excluded (exclude takes precedence)
|
|
56
56
|
* 4. Bundle order from config is preserved
|
|
57
57
|
*
|
|
58
|
-
* @param
|
|
58
|
+
* @param pageUrl - The page URL (e.g., '/docs/api/hooks'), not its output file
|
|
59
59
|
* @param bundles - Array of bundle configurations to filter
|
|
60
60
|
* @returns Array of bundles that match this page, in config order
|
|
61
61
|
*
|
|
@@ -67,22 +67,22 @@ export interface CompiledBundleInfo {
|
|
|
67
67
|
* { entryPoint: 'playground.ts', bundleName: 'playground', include: ['/examples/**'], exclude: ['/examples/simple/**'] }
|
|
68
68
|
* ];
|
|
69
69
|
*
|
|
70
|
-
* matchBundlesForPage('/docs/api/hooks
|
|
70
|
+
* matchBundlesForPage('/docs/api/hooks', bundles);
|
|
71
71
|
* // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'docs.ts', ... }]
|
|
72
72
|
*
|
|
73
|
-
* matchBundlesForPage('/examples/advanced/demo
|
|
73
|
+
* matchBundlesForPage('/examples/advanced/demo', bundles);
|
|
74
74
|
* // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'playground.ts', ... }]
|
|
75
75
|
*
|
|
76
|
-
* matchBundlesForPage('/examples/simple/basic
|
|
76
|
+
* matchBundlesForPage('/examples/simple/basic', bundles);
|
|
77
77
|
* // Returns: [{ entryPoint: 'core.ts', ... }] - playground excluded
|
|
78
78
|
* ```
|
|
79
79
|
*/
|
|
80
|
-
export declare function matchBundlesForPage(
|
|
80
|
+
export declare function matchBundlesForPage(pageUrl: string, bundles: BundleConfig[]): BundleConfig[];
|
|
81
81
|
/**
|
|
82
82
|
* Gets bundle paths for a page from compiled bundle info.
|
|
83
83
|
* Filters compiled bundles based on page matching and returns only the paths.
|
|
84
84
|
*
|
|
85
|
-
* @param
|
|
85
|
+
* @param pageUrl - The page URL (e.g., '/docs/api/hooks'), not its output file
|
|
86
86
|
* @param compiledBundles - Array of compiled bundle info
|
|
87
87
|
* @returns Array of bundle paths matched for this page, in config order
|
|
88
88
|
*
|
|
@@ -93,9 +93,9 @@ export declare function matchBundlesForPage(pageOutputPath: string, bundles: Bun
|
|
|
93
93
|
* { config: { entryPoint: 'docs.ts', bundleName: 'docs', include: ['/docs/**'] }, filename: 'docs-def.js', path: '/_assets/docs-def.js' }
|
|
94
94
|
* ];
|
|
95
95
|
*
|
|
96
|
-
* getBundlePathsForPage('/docs/api/hooks
|
|
96
|
+
* getBundlePathsForPage('/docs/api/hooks', compiled);
|
|
97
97
|
* // Returns: ['/_assets/core-abc.js', '/_assets/docs-def.js']
|
|
98
98
|
* ```
|
|
99
99
|
*/
|
|
100
|
-
export declare function getBundlePathsForPage(
|
|
100
|
+
export declare function getBundlePathsForPage(pageUrl: string, compiledBundles: CompiledBundleInfo[]): string[];
|
|
101
101
|
//# sourceMappingURL=bundle-matching.utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-matching.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/bundle-matching.utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1D;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,UAAU,EAAE,MAAM,EAAE;CAOjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAqBvE;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,MAAM,EAAE,YAAY,CAAC;IACrB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"bundle-matching.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/bundle-matching.utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1D;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,UAAU,EAAE,MAAM,EAAE;CAOjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAqBvE;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,MAAM,EAAE,YAAY,CAAC;IACrB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAyB5F;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,kBAAkB,EAAE,GACpC,MAAM,EAAE,CAaV"}
|
|
@@ -64,7 +64,7 @@ export function validateUniqueBundleNames(bundles) {
|
|
|
64
64
|
* 3. If page matches any `exclude` pattern: bundle is excluded (exclude takes precedence)
|
|
65
65
|
* 4. Bundle order from config is preserved
|
|
66
66
|
*
|
|
67
|
-
* @param
|
|
67
|
+
* @param pageUrl - The page URL (e.g., '/docs/api/hooks'), not its output file
|
|
68
68
|
* @param bundles - Array of bundle configurations to filter
|
|
69
69
|
* @returns Array of bundles that match this page, in config order
|
|
70
70
|
*
|
|
@@ -76,22 +76,22 @@ export function validateUniqueBundleNames(bundles) {
|
|
|
76
76
|
* { entryPoint: 'playground.ts', bundleName: 'playground', include: ['/examples/**'], exclude: ['/examples/simple/**'] }
|
|
77
77
|
* ];
|
|
78
78
|
*
|
|
79
|
-
* matchBundlesForPage('/docs/api/hooks
|
|
79
|
+
* matchBundlesForPage('/docs/api/hooks', bundles);
|
|
80
80
|
* // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'docs.ts', ... }]
|
|
81
81
|
*
|
|
82
|
-
* matchBundlesForPage('/examples/advanced/demo
|
|
82
|
+
* matchBundlesForPage('/examples/advanced/demo', bundles);
|
|
83
83
|
* // Returns: [{ entryPoint: 'core.ts', ... }, { entryPoint: 'playground.ts', ... }]
|
|
84
84
|
*
|
|
85
|
-
* matchBundlesForPage('/examples/simple/basic
|
|
85
|
+
* matchBundlesForPage('/examples/simple/basic', bundles);
|
|
86
86
|
* // Returns: [{ entryPoint: 'core.ts', ... }] - playground excluded
|
|
87
87
|
* ```
|
|
88
88
|
*/
|
|
89
|
-
export function matchBundlesForPage(
|
|
89
|
+
export function matchBundlesForPage(pageUrl, bundles) {
|
|
90
90
|
if (!bundles || bundles.length === 0) {
|
|
91
91
|
return [];
|
|
92
92
|
}
|
|
93
93
|
// Normalize path to use forward slashes
|
|
94
|
-
const normalizedPath =
|
|
94
|
+
const normalizedPath = pageUrl.replace(/\\/g, '/');
|
|
95
95
|
return bundles.filter((bundle) => {
|
|
96
96
|
// Check exclude patterns first (they take precedence)
|
|
97
97
|
if (bundle.exclude && bundle.exclude.length > 0) {
|
|
@@ -112,7 +112,7 @@ export function matchBundlesForPage(pageOutputPath, bundles) {
|
|
|
112
112
|
* Gets bundle paths for a page from compiled bundle info.
|
|
113
113
|
* Filters compiled bundles based on page matching and returns only the paths.
|
|
114
114
|
*
|
|
115
|
-
* @param
|
|
115
|
+
* @param pageUrl - The page URL (e.g., '/docs/api/hooks'), not its output file
|
|
116
116
|
* @param compiledBundles - Array of compiled bundle info
|
|
117
117
|
* @returns Array of bundle paths matched for this page, in config order
|
|
118
118
|
*
|
|
@@ -123,15 +123,15 @@ export function matchBundlesForPage(pageOutputPath, bundles) {
|
|
|
123
123
|
* { config: { entryPoint: 'docs.ts', bundleName: 'docs', include: ['/docs/**'] }, filename: 'docs-def.js', path: '/_assets/docs-def.js' }
|
|
124
124
|
* ];
|
|
125
125
|
*
|
|
126
|
-
* getBundlePathsForPage('/docs/api/hooks
|
|
126
|
+
* getBundlePathsForPage('/docs/api/hooks', compiled);
|
|
127
127
|
* // Returns: ['/_assets/core-abc.js', '/_assets/docs-def.js']
|
|
128
128
|
* ```
|
|
129
129
|
*/
|
|
130
|
-
export function getBundlePathsForPage(
|
|
130
|
+
export function getBundlePathsForPage(pageUrl, compiledBundles) {
|
|
131
131
|
if (!compiledBundles || compiledBundles.length === 0) {
|
|
132
132
|
return [];
|
|
133
133
|
}
|
|
134
|
-
const matchedConfigs = matchBundlesForPage(
|
|
134
|
+
const matchedConfigs = matchBundlesForPage(pageUrl, compiledBundles.map((b) => b.config));
|
|
135
135
|
// Map matched configs back to their paths, preserving order
|
|
136
136
|
const matchedNames = new Set(matchedConfigs.map((c) => c.bundleName));
|
|
137
137
|
return compiledBundles.filter((b) => matchedNames.has(b.config.bundleName)).map((b) => b.path);
|
|
@@ -24,6 +24,14 @@ export declare function remove(path: string): Promise<void>;
|
|
|
24
24
|
* Gets file stats with consistent error handling.
|
|
25
25
|
*/
|
|
26
26
|
export declare function stat(filePath: string): Promise<fse.Stats>;
|
|
27
|
+
/**
|
|
28
|
+
* Gets file stats without following a symbolic link at the path, with consistent error handling.
|
|
29
|
+
*/
|
|
30
|
+
export declare function lstat(filePath: string): Promise<fse.Stats>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a path to its real location, following symbolic links, with consistent error handling.
|
|
33
|
+
*/
|
|
34
|
+
export declare function realpath(filePath: string): Promise<string>;
|
|
27
35
|
/**
|
|
28
36
|
* Reads directory contents with consistent error handling.
|
|
29
37
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/fs.utils.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"fs.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/fs.utils.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAqDvD;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,OAAO,GAAG,MAAgB,GACnC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnE;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExD;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAE/D;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAEhE;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhE;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,CAAC,SAAS,OAAO,GAAG,KAAK,EACrD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,CAAC,CAAA;CAAE,GAC9B,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAcnD;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE7E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUlE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAU7D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAchE"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fse from 'fs-extra';
|
|
2
2
|
import { readFileSync, statSync, readdirSync } from 'node:fs';
|
|
3
3
|
import { join, relative } from 'node:path';
|
|
4
|
-
const { readFile: fseReadFile, writeFile: fseWriteFile, pathExists: fsePathExists, ensureDir: fseEnsureDir, remove: fseRemove, stat: fseStat, readdir: fseReaddir, copyFile: fseCopyFile, } = fse;
|
|
4
|
+
const { readFile: fseReadFile, writeFile: fseWriteFile, pathExists: fsePathExists, ensureDir: fseEnsureDir, remove: fseRemove, stat: fseStat, lstat: fseLstat, realpath: fseRealpath, readdir: fseReaddir, copyFile: fseCopyFile, } = fse;
|
|
5
5
|
/**
|
|
6
6
|
* Wraps fs-extra operations with consistent error handling.
|
|
7
7
|
*/
|
|
@@ -65,6 +65,18 @@ export async function remove(path) {
|
|
|
65
65
|
export async function stat(filePath) {
|
|
66
66
|
return wrapFsOperation('get stats for', fseStat(filePath), filePath);
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Gets file stats without following a symbolic link at the path, with consistent error handling.
|
|
70
|
+
*/
|
|
71
|
+
export async function lstat(filePath) {
|
|
72
|
+
return wrapFsOperation('get stats for', fseLstat(filePath), filePath);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolves a path to its real location, following symbolic links, with consistent error handling.
|
|
76
|
+
*/
|
|
77
|
+
export async function realpath(filePath) {
|
|
78
|
+
return wrapFsOperation('resolve', fseRealpath(filePath), filePath);
|
|
79
|
+
}
|
|
68
80
|
/**
|
|
69
81
|
* Reads directory contents with consistent error handling.
|
|
70
82
|
*/
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Core utilities index
|
|
3
3
|
* @module core/utils
|
|
4
4
|
*/
|
|
5
|
-
export { readFile, writeFile, pathExists, ensureDir, remove, copyFile, readdir, stat, readFileBuffer, readFileBufferSync, statSyncOrNull, listFilesRecursiveSync, } from './fs.utils.js';
|
|
6
|
-
export { resolveSrcDir, resolveOutDir, resolveStaticDir, resolveCacheDir, resolveDevPaths, normalizeTemplatePath, resolveSrcPath, resolveOutPath, resolveStaticPath, normalizePathForComparison, isPathWithinDirectory, } from './paths.utils.js';
|
|
7
|
-
export { discoverLayout, isCollectionIndexPage, getCollectionPathForPage, } from './template-discovery.utils.js';
|
|
5
|
+
export { readFile, writeFile, pathExists, ensureDir, remove, copyFile, readdir, stat, lstat, realpath, readFileBuffer, readFileBufferSync, statSyncOrNull, listFilesRecursiveSync, } from './fs.utils.js';
|
|
6
|
+
export { resolveSrcDir, resolveOutDir, resolveStaticDir, resolveCacheDir, resolveDevPaths, normalizeTemplatePath, resolveSrcPath, resolveOutPath, resolveStaticPath, normalizePathForComparison, isPathWithinDirectory, getPageOutputFile, } from './paths.utils.js';
|
|
7
|
+
export { discoverLayout, isCollectionIndexPage, isVirtualPage, getFirstPaginationPage, getCollectionPathForPage, } from './template-discovery.utils.js';
|
|
8
8
|
export { propValue } from './template.utils.js';
|
|
9
9
|
export { slugify } from './slugify.utils.js';
|
|
10
10
|
export { trackTailwindClass, enableInventoryTracking, disableInventoryTracking, clearInventory, getInventory, getInventorySize, isTrackingEnabled, writeTailwindClassInventory, isTailwindUsed, resetTailwindDetection, loadPreviousInventory, } from './tailwind-inventory.utils.js';
|
|
@@ -15,7 +15,7 @@ export { makeCallablePartial, wrapPartialsAsCallable } from './callable-partials
|
|
|
15
15
|
export type { CallablePartial } from './callable-partials.utils.js';
|
|
16
16
|
export { TemplateError, parseEtaError, createTemplateError } from './template-errors.utils.js';
|
|
17
17
|
export { createNavigationHelpers } from './navigation-helpers.utils.js';
|
|
18
|
-
export { resolvePrettyUrl, mergeServerOptions, assertPortAvailable, toPortUnavailableError, PortUnavailableError, } from './server.utils.js';
|
|
18
|
+
export { getMimeType, parseRequestPath, resolveNotFoundPage, resolveRequestFile, resolvePrettyUrl, mergeServerOptions, assertPortAvailable, toPortUnavailableError, PortUnavailableError, } from './server.utils.js';
|
|
19
19
|
export type { PrettyUrlResult, MergedServerOptions, MergeServerOptionsParams, PortUnavailableCode, } from './server.utils.js';
|
|
20
20
|
export { createErrorOverlay, parseErrorDetails } from './error-overlay.utils.js';
|
|
21
21
|
export type { ErrorDetails } from './error-overlay.utils.js';
|
|
@@ -24,8 +24,8 @@ export { globToRegex, matchesGlob } from './glob-patterns.utils.js';
|
|
|
24
24
|
export { matchBundlesForPage, getBundlePathsForPage, validateUniqueBundleNames, DuplicateBundleNameError, } from './bundle-matching.utils.js';
|
|
25
25
|
export type { CompiledBundleInfo } from './bundle-matching.utils.js';
|
|
26
26
|
export { createFallbackLogger } from './logger.utils.js';
|
|
27
|
-
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, } from './typescript.utils.js';
|
|
28
|
-
export type { CompileOptions, WatchOptions } from './typescript.utils.js';
|
|
27
|
+
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, removeStaleBundleFiles, } from './typescript.utils.js';
|
|
28
|
+
export type { CompileOptions, WatchOptions, RemoveStaleBundlesOptions, } from './typescript.utils.js';
|
|
29
29
|
export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
|
|
30
30
|
export { getFrontMatterDate, PUBLISHED_DATE_FIELDS } from './date.utils.js';
|
|
31
31
|
export { isEnabledEnvFlag } from './env.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,EACJ,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,sBAAsB,GACvB,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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"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"}
|
package/dist/core/utils/index.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* @module core/utils
|
|
4
4
|
*/
|
|
5
5
|
// File system utilities
|
|
6
|
-
export { readFile, writeFile, pathExists, ensureDir, remove, copyFile, readdir, stat, readFileBuffer, readFileBufferSync, statSyncOrNull, listFilesRecursiveSync, } from './fs.utils.js';
|
|
6
|
+
export { readFile, writeFile, pathExists, ensureDir, remove, copyFile, readdir, stat, lstat, realpath, readFileBuffer, readFileBufferSync, statSyncOrNull, listFilesRecursiveSync, } from './fs.utils.js';
|
|
7
7
|
// Path resolution utilities
|
|
8
|
-
export { resolveSrcDir, resolveOutDir, resolveStaticDir, resolveCacheDir, resolveDevPaths, normalizeTemplatePath, resolveSrcPath, resolveOutPath, resolveStaticPath, normalizePathForComparison, isPathWithinDirectory, } from './paths.utils.js';
|
|
8
|
+
export { resolveSrcDir, resolveOutDir, resolveStaticDir, resolveCacheDir, resolveDevPaths, normalizeTemplatePath, resolveSrcPath, resolveOutPath, resolveStaticPath, normalizePathForComparison, isPathWithinDirectory, getPageOutputFile, } from './paths.utils.js';
|
|
9
9
|
// Template discovery utilities
|
|
10
|
-
export { discoverLayout, isCollectionIndexPage, getCollectionPathForPage, } from './template-discovery.utils.js';
|
|
10
|
+
export { discoverLayout, isCollectionIndexPage, isVirtualPage, getFirstPaginationPage, getCollectionPathForPage, } from './template-discovery.utils.js';
|
|
11
11
|
// Template utilities
|
|
12
12
|
export { propValue } from './template.utils.js';
|
|
13
13
|
// Slugify utilities
|
|
@@ -27,7 +27,7 @@ export { TemplateError, parseEtaError, createTemplateError } from './template-er
|
|
|
27
27
|
// Navigation helper utilities
|
|
28
28
|
export { createNavigationHelpers } from './navigation-helpers.utils.js';
|
|
29
29
|
// Server utilities
|
|
30
|
-
export { resolvePrettyUrl, mergeServerOptions, assertPortAvailable, toPortUnavailableError, PortUnavailableError, } from './server.utils.js';
|
|
30
|
+
export { getMimeType, parseRequestPath, resolveNotFoundPage, resolveRequestFile, resolvePrettyUrl, mergeServerOptions, assertPortAvailable, toPortUnavailableError, PortUnavailableError, } from './server.utils.js';
|
|
31
31
|
// Error overlay utilities
|
|
32
32
|
export { createErrorOverlay, parseErrorDetails } from './error-overlay.utils.js';
|
|
33
33
|
// Version utilities
|
|
@@ -39,7 +39,7 @@ export { matchBundlesForPage, getBundlePathsForPage, validateUniqueBundleNames,
|
|
|
39
39
|
// Logger utilities
|
|
40
40
|
export { createFallbackLogger } from './logger.utils.js';
|
|
41
41
|
// TypeScript compilation utilities
|
|
42
|
-
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, } from './typescript.utils.js';
|
|
42
|
+
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, removeStaleBundleFiles, } from './typescript.utils.js';
|
|
43
43
|
// HTML manipulation utilities
|
|
44
44
|
export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
|
|
45
45
|
// Date utilities
|
|
@@ -131,4 +131,19 @@ export declare function normalizePathForComparison(filePath: string, basePath?:
|
|
|
131
131
|
* ```
|
|
132
132
|
*/
|
|
133
133
|
export declare function isPathWithinDirectory(baseDir: string, targetPath: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* The file a page URL is written to, relative to the output directory. The build joins it to
|
|
136
|
+
* the output directory, which also normalizes separators and `.` segments.
|
|
137
|
+
*
|
|
138
|
+
* @param url - The page URL
|
|
139
|
+
* @returns The output file
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* getPageOutputFile('/'); // 'index.html'
|
|
144
|
+
* getPageOutputFile('/blog/'); // '/blog/index.html'
|
|
145
|
+
* getPageOutputFile('/blog/post'); // '/blog/post.html'
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export declare function getPageOutputFile(url: string): string;
|
|
134
149
|
//# sourceMappingURL=paths.utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/paths.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQxD;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW;;;;EAMlD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BtF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAYlF"}
|
|
1
|
+
{"version":3,"file":"paths.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/paths.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQxD;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW;;;;EAMlD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BtF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAYlF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKrD"}
|
|
@@ -183,3 +183,23 @@ export function isPathWithinDirectory(baseDir, targetPath) {
|
|
|
183
183
|
// We also allow exact match (resolvedTarget === resolvedBase) for the root
|
|
184
184
|
return resolvedTarget === resolvedBase || resolvedTarget.startsWith(normalizedBase);
|
|
185
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* The file a page URL is written to, relative to the output directory. The build joins it to
|
|
188
|
+
* the output directory, which also normalizes separators and `.` segments.
|
|
189
|
+
*
|
|
190
|
+
* @param url - The page URL
|
|
191
|
+
* @returns The output file
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* getPageOutputFile('/'); // 'index.html'
|
|
196
|
+
* getPageOutputFile('/blog/'); // '/blog/index.html'
|
|
197
|
+
* getPageOutputFile('/blog/post'); // '/blog/post.html'
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export function getPageOutputFile(url) {
|
|
201
|
+
if (url === '/') {
|
|
202
|
+
return 'index.html';
|
|
203
|
+
}
|
|
204
|
+
return url.endsWith('/') ? `${url}index.html` : `${url}.html`;
|
|
205
|
+
}
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets the content type for a file from its extension.
|
|
3
|
+
*
|
|
4
|
+
* @param filePath - Path or file name of the file being served
|
|
5
|
+
* @returns The content type, or `application/octet-stream` for unknown extensions
|
|
6
|
+
*/
|
|
7
|
+
export declare function getMimeType(filePath: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Turns a request URL into the path of the file it asks for: the query string is dropped and
|
|
10
|
+
* percent-escapes are decoded, so `/about?ref=x` asks for `/about` and `/my%20file.pdf` for
|
|
11
|
+
* `/my file.pdf`.
|
|
12
|
+
*
|
|
13
|
+
* @param url - The request URL as received (`req.url`)
|
|
14
|
+
* @returns The decoded path, or null when the URL is malformed (a bad escape or a NUL byte)
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseRequestPath(url: string | undefined): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Finds the site's custom not-found page, the one static hosts serve for a missing URL.
|
|
19
|
+
*
|
|
20
|
+
* @param outDir - The output directory being served
|
|
21
|
+
* @returns The path of `404.html` (or `404/index.html` for a `/404/` permalink), or null
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveNotFoundPage(outDir: string): Promise<string | null>;
|
|
1
24
|
/**
|
|
2
25
|
* Error codes for which a port is considered unavailable
|
|
3
26
|
*/
|
|
@@ -42,6 +65,17 @@ export interface PrettyUrlResult {
|
|
|
42
65
|
/** Whether the path was found */
|
|
43
66
|
found: boolean;
|
|
44
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolves a request path to the file it names inside the served directory. This is where a
|
|
70
|
+
* request path becomes a file system path: every file the servers read is this path or
|
|
71
|
+
* derived from it, so no request reaches outside the directory, whether through `..`
|
|
72
|
+
* segments (raw or percent-encoded), backslashes or an absolute path.
|
|
73
|
+
*
|
|
74
|
+
* @param outDir - The directory being served
|
|
75
|
+
* @param requestPath - The decoded request path, from {@link parseRequestPath}
|
|
76
|
+
* @returns The absolute path, or null when it points outside the directory
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveRequestFile(outDir: string, requestPath: string): string | null;
|
|
45
79
|
/**
|
|
46
80
|
* Attempts to resolve pretty URL paths by trying various fallback strategies.
|
|
47
81
|
* This handles common patterns like:
|
|
@@ -50,11 +84,12 @@ export interface PrettyUrlResult {
|
|
|
50
84
|
* - /path -> /path.html (when original path is not found and has no extension)
|
|
51
85
|
*
|
|
52
86
|
* @param outDir The output directory to serve files from
|
|
53
|
-
* @param requestPath The requested URL path
|
|
54
|
-
* @param
|
|
87
|
+
* @param requestPath The requested URL path, which decides the fallbacks
|
|
88
|
+
* @param requestFile The file the request names, from {@link resolveRequestFile}. Every
|
|
89
|
+
* candidate is derived from it, so none is outside the output directory
|
|
55
90
|
* @returns Promise resolving to the file path to serve or null if not found
|
|
56
91
|
*/
|
|
57
|
-
export declare function resolvePrettyUrl(outDir: string, requestPath: string,
|
|
92
|
+
export declare function resolvePrettyUrl(outDir: string, requestPath: string, requestFile: string): Promise<PrettyUrlResult>;
|
|
58
93
|
/**
|
|
59
94
|
* Options for merging server configuration
|
|
60
95
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/server.utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/server.utils.ts"],"names":[],"mappings":"AAsCA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQvE;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWhF;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,QAAQ,CAAC;AAE1D;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;gBAEvB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,MAAM;CAatF;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAMT;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjG;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iCAAiC;IACjC,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWrF;AAkBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CA8B1B;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC,sCAAsC;IACtC,OAAO,EAAE,CAAC,CAAC;IACX,4CAA4C;IAC5C,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IACrE,4EAA4E;IAC5E,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,OAAO,CAAC;KACf,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EAC3F,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAClC,mBAAmB,CAQrB"}
|
|
@@ -1,6 +1,85 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
1
|
+
import { extname, join, resolve, sep } from 'node:path';
|
|
2
2
|
import { stat } from 'node:fs/promises';
|
|
3
3
|
import { createServer } from 'node:net';
|
|
4
|
+
/**
|
|
5
|
+
* Content types the dev and preview servers send, by file extension. Covers the files Stati
|
|
6
|
+
* generates (pages, bundles, search index, sitemap, robots.txt, feeds, responsive images) and
|
|
7
|
+
* common static assets; anything else is sent as `application/octet-stream`.
|
|
8
|
+
*/
|
|
9
|
+
const MIME_TYPES = {
|
|
10
|
+
'.html': 'text/html',
|
|
11
|
+
'.js': 'application/javascript',
|
|
12
|
+
'.mjs': 'application/javascript',
|
|
13
|
+
'.css': 'text/css',
|
|
14
|
+
'.json': 'application/json',
|
|
15
|
+
'.map': 'application/json',
|
|
16
|
+
'.webmanifest': 'application/manifest+json',
|
|
17
|
+
'.xml': 'application/xml',
|
|
18
|
+
'.txt': 'text/plain',
|
|
19
|
+
'.png': 'image/png',
|
|
20
|
+
'.jpg': 'image/jpeg',
|
|
21
|
+
'.jpeg': 'image/jpeg',
|
|
22
|
+
'.gif': 'image/gif',
|
|
23
|
+
'.svg': 'image/svg+xml',
|
|
24
|
+
'.ico': 'image/x-icon',
|
|
25
|
+
'.webp': 'image/webp',
|
|
26
|
+
'.avif': 'image/avif',
|
|
27
|
+
'.woff': 'font/woff',
|
|
28
|
+
'.woff2': 'font/woff2',
|
|
29
|
+
'.ttf': 'font/ttf',
|
|
30
|
+
'.otf': 'font/otf',
|
|
31
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
32
|
+
'.pdf': 'application/pdf',
|
|
33
|
+
'.mp4': 'video/mp4',
|
|
34
|
+
'.webm': 'video/webm',
|
|
35
|
+
'.wasm': 'application/wasm',
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Gets the content type for a file from its extension.
|
|
39
|
+
*
|
|
40
|
+
* @param filePath - Path or file name of the file being served
|
|
41
|
+
* @returns The content type, or `application/octet-stream` for unknown extensions
|
|
42
|
+
*/
|
|
43
|
+
export function getMimeType(filePath) {
|
|
44
|
+
return MIME_TYPES[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Turns a request URL into the path of the file it asks for: the query string is dropped and
|
|
48
|
+
* percent-escapes are decoded, so `/about?ref=x` asks for `/about` and `/my%20file.pdf` for
|
|
49
|
+
* `/my file.pdf`.
|
|
50
|
+
*
|
|
51
|
+
* @param url - The request URL as received (`req.url`)
|
|
52
|
+
* @returns The decoded path, or null when the URL is malformed (a bad escape or a NUL byte)
|
|
53
|
+
*/
|
|
54
|
+
export function parseRequestPath(url) {
|
|
55
|
+
const rawPath = (url || '/').split(/[?#]/, 1)[0] || '/';
|
|
56
|
+
try {
|
|
57
|
+
const path = decodeURIComponent(rawPath);
|
|
58
|
+
return path.includes('\0') ? null : path;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Finds the site's custom not-found page, the one static hosts serve for a missing URL.
|
|
66
|
+
*
|
|
67
|
+
* @param outDir - The output directory being served
|
|
68
|
+
* @returns The path of `404.html` (or `404/index.html` for a `/404/` permalink), or null
|
|
69
|
+
*/
|
|
70
|
+
export async function resolveNotFoundPage(outDir) {
|
|
71
|
+
for (const candidate of [join(outDir, '404.html'), join(outDir, '404', 'index.html')]) {
|
|
72
|
+
try {
|
|
73
|
+
if ((await stat(candidate)).isFile()) {
|
|
74
|
+
return candidate;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Not there; try the next one
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
4
83
|
/**
|
|
5
84
|
* Thrown when a server cannot bind to its configured port.
|
|
6
85
|
* The message is user-facing and explains how to pick a different port.
|
|
@@ -59,6 +138,44 @@ export function assertPortAvailable(port, host, configKey) {
|
|
|
59
138
|
probe.listen(port, host);
|
|
60
139
|
});
|
|
61
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Resolves a request path to the file it names inside the served directory. This is where a
|
|
143
|
+
* request path becomes a file system path: every file the servers read is this path or
|
|
144
|
+
* derived from it, so no request reaches outside the directory, whether through `..`
|
|
145
|
+
* segments (raw or percent-encoded), backslashes or an absolute path.
|
|
146
|
+
*
|
|
147
|
+
* @param outDir - The directory being served
|
|
148
|
+
* @param requestPath - The decoded request path, from {@link parseRequestPath}
|
|
149
|
+
* @returns The absolute path, or null when it points outside the directory
|
|
150
|
+
*/
|
|
151
|
+
export function resolveRequestFile(outDir, requestPath) {
|
|
152
|
+
const root = resolve(outDir);
|
|
153
|
+
const filePath = resolve(root, requestPath.replace(/^[/\\]+/, ''));
|
|
154
|
+
if (filePath === root) {
|
|
155
|
+
return root;
|
|
156
|
+
}
|
|
157
|
+
const rootPrefix = root.endsWith(sep) ? root : root + sep;
|
|
158
|
+
if (!filePath.startsWith(rootPrefix)) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return filePath;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The result for a candidate file: found when it exists and is a file.
|
|
165
|
+
*/
|
|
166
|
+
async function fileResult(candidate) {
|
|
167
|
+
if (candidate !== null) {
|
|
168
|
+
try {
|
|
169
|
+
if ((await stat(candidate)).isFile()) {
|
|
170
|
+
return { filePath: candidate, found: true };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Not there
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { filePath: null, found: false };
|
|
178
|
+
}
|
|
62
179
|
/**
|
|
63
180
|
* Attempts to resolve pretty URL paths by trying various fallback strategies.
|
|
64
181
|
* This handles common patterns like:
|
|
@@ -67,16 +184,20 @@ export function assertPortAvailable(port, host, configKey) {
|
|
|
67
184
|
* - /path -> /path.html (when original path is not found and has no extension)
|
|
68
185
|
*
|
|
69
186
|
* @param outDir The output directory to serve files from
|
|
70
|
-
* @param requestPath The requested URL path
|
|
71
|
-
* @param
|
|
187
|
+
* @param requestPath The requested URL path, which decides the fallbacks
|
|
188
|
+
* @param requestFile The file the request names, from {@link resolveRequestFile}. Every
|
|
189
|
+
* candidate is derived from it, so none is outside the output directory
|
|
72
190
|
* @returns Promise resolving to the file path to serve or null if not found
|
|
73
191
|
*/
|
|
74
|
-
export async function resolvePrettyUrl(outDir, requestPath,
|
|
192
|
+
export async function resolvePrettyUrl(outDir, requestPath, requestFile) {
|
|
193
|
+
// The sibling .html file: /blog/ and /blog are served from blog.html. The output
|
|
194
|
+
// directory itself has none, as its sibling is outside it.
|
|
195
|
+
const htmlFile = requestFile === resolve(outDir) ? null : `${requestFile}.html`;
|
|
75
196
|
try {
|
|
76
|
-
const stats = await stat(
|
|
197
|
+
const stats = await stat(requestFile);
|
|
77
198
|
if (stats.isDirectory()) {
|
|
78
199
|
// Try to serve index.html from directory
|
|
79
|
-
const indexPath = join(
|
|
200
|
+
const indexPath = join(requestFile, 'index.html');
|
|
80
201
|
try {
|
|
81
202
|
await stat(indexPath);
|
|
82
203
|
return { filePath: indexPath, found: true };
|
|
@@ -84,50 +205,17 @@ export async function resolvePrettyUrl(outDir, requestPath, originalFilePath) {
|
|
|
84
205
|
catch {
|
|
85
206
|
// If no index.html in directory, try to serve corresponding .html file
|
|
86
207
|
// For example: /examples/ -> examples.html
|
|
87
|
-
|
|
88
|
-
const fallbackPath = join(outDir, `${directoryName}.html`);
|
|
89
|
-
try {
|
|
90
|
-
await stat(fallbackPath);
|
|
91
|
-
return { filePath: fallbackPath, found: true };
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
return { filePath: null, found: false };
|
|
95
|
-
}
|
|
208
|
+
return await fileResult(htmlFile);
|
|
96
209
|
}
|
|
97
210
|
}
|
|
98
211
|
// File exists and is not a directory
|
|
99
|
-
return { filePath:
|
|
212
|
+
return { filePath: requestFile, found: true };
|
|
100
213
|
}
|
|
101
214
|
catch {
|
|
102
|
-
// File not found
|
|
103
|
-
if (requestPath.endsWith('/')) {
|
|
104
|
-
|
|
105
|
-
const pathWithoutSlash = requestPath.slice(0, -1);
|
|
106
|
-
const htmlPath = join(outDir, `${pathWithoutSlash}.html`);
|
|
107
|
-
try {
|
|
108
|
-
const stats = await stat(htmlPath);
|
|
109
|
-
if (stats.isFile()) {
|
|
110
|
-
return { filePath: htmlPath, found: true };
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
// Continue to not found
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else if (!requestPath.includes('.')) {
|
|
118
|
-
// For requests without trailing slash and without extension, try .html
|
|
119
|
-
const htmlPath = join(outDir, `${requestPath}.html`);
|
|
120
|
-
try {
|
|
121
|
-
const stats = await stat(htmlPath);
|
|
122
|
-
if (stats.isFile()) {
|
|
123
|
-
return { filePath: htmlPath, found: true };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
// Continue to not found
|
|
128
|
-
}
|
|
215
|
+
// File not found: try the .html file for /path/ and for /path without an extension
|
|
216
|
+
if (requestPath.endsWith('/') || !requestPath.includes('.')) {
|
|
217
|
+
return await fileResult(htmlFile);
|
|
129
218
|
}
|
|
130
|
-
// No fallback worked
|
|
131
219
|
return { filePath: null, found: false };
|
|
132
220
|
}
|
|
133
221
|
}
|
|
@@ -1,13 +1,37 @@
|
|
|
1
1
|
import type { StatiConfig, PageModel } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared template discovery utilities.
|
|
4
|
+
* Extracted from templates.ts and isg/deps.ts to eliminate duplication.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Whether a page was created by Stati rather than loaded from a Markdown file: a page expanded
|
|
8
|
+
* from a `[param].md` template, or page 2 or later of a paginated listing.
|
|
9
|
+
*
|
|
10
|
+
* Virtual pages are never collection members, never make a folder a collection, and are left
|
|
11
|
+
* out of navigation, search and RSS unless they opt in.
|
|
12
|
+
*
|
|
13
|
+
* @param page - The page to check
|
|
14
|
+
* @returns True for generated pages and for pagination pages after the first
|
|
15
|
+
*/
|
|
16
|
+
export declare function isVirtualPage(page: PageModel): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Finds the first page of a paginated listing, which later pages render like.
|
|
19
|
+
*
|
|
20
|
+
* @param page - A page with `pagination`
|
|
21
|
+
* @param allPages - All pages in the site
|
|
22
|
+
* @returns The listing's first page, or undefined when it is not in the list
|
|
23
|
+
*/
|
|
24
|
+
export declare function getFirstPaginationPage(page: PageModel, allPages: PageModel[]): PageModel | undefined;
|
|
2
25
|
/**
|
|
3
26
|
* Determines if the given page is a collection index page.
|
|
4
27
|
* A collection index page is one whose URL matches a directory path that contains other pages.
|
|
28
|
+
* Generated pages never are; pagination pages after the first are when their first page is.
|
|
5
29
|
*
|
|
6
30
|
* Rendering and ISG dependency tracking must both call this with the same page list,
|
|
7
31
|
* otherwise they can pick different layouts for the same page.
|
|
8
32
|
*
|
|
9
33
|
* Page URLs are read once per `allPages` array, at the first call; see
|
|
10
|
-
* `
|
|
34
|
+
* `pageListCache` for when changes to the pages are picked up.
|
|
11
35
|
*
|
|
12
36
|
* @param page - The page to check
|
|
13
37
|
* @param allPages - All pages in the site (treated as unchanging after the first call)
|