@stati/core 1.20.3 → 1.22.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.
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +49 -4
- package/dist/core/build.d.ts +6 -0
- package/dist/core/build.d.ts.map +1 -1
- package/dist/core/build.js +176 -66
- package/dist/core/dev.d.ts.map +1 -1
- package/dist/core/dev.js +100 -28
- package/dist/core/isg/builder.d.ts +4 -1
- package/dist/core/isg/builder.d.ts.map +1 -1
- package/dist/core/isg/builder.js +89 -2
- package/dist/core/isg/deps.d.ts +5 -0
- package/dist/core/isg/deps.d.ts.map +1 -1
- package/dist/core/isg/deps.js +38 -3
- package/dist/core/isg/dev-server-lock.d.ts +85 -0
- package/dist/core/isg/dev-server-lock.d.ts.map +1 -0
- package/dist/core/isg/dev-server-lock.js +248 -0
- package/dist/core/isg/hash.d.ts +4 -0
- package/dist/core/isg/hash.d.ts.map +1 -1
- package/dist/core/isg/hash.js +24 -1
- package/dist/core/isg/index.d.ts +3 -2
- package/dist/core/isg/index.d.ts.map +1 -1
- package/dist/core/isg/index.js +3 -2
- package/dist/core/markdown.d.ts +6 -0
- package/dist/core/markdown.d.ts.map +1 -1
- package/dist/core/markdown.js +23 -0
- package/dist/core/preview.js +1 -1
- package/dist/core/templates.js +5 -5
- package/dist/core/utils/bundle-matching.utils.d.ts +2 -0
- package/dist/core/utils/bundle-matching.utils.d.ts.map +1 -1
- package/dist/core/utils/index.d.ts +1 -1
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core/utils/index.js +1 -1
- package/dist/core/utils/logger.utils.d.ts.map +1 -1
- package/dist/core/utils/logger.utils.js +1 -0
- package/dist/core/utils/partial-validation.utils.js +2 -2
- package/dist/core/utils/paths.utils.d.ts +18 -0
- package/dist/core/utils/paths.utils.d.ts.map +1 -1
- package/dist/core/utils/paths.utils.js +23 -0
- package/dist/core/utils/tailwind-inventory.utils.d.ts +1 -16
- package/dist/core/utils/tailwind-inventory.utils.d.ts.map +1 -1
- package/dist/core/utils/tailwind-inventory.utils.js +35 -3
- package/dist/core/utils/typescript.utils.d.ts +13 -0
- package/dist/core/utils/typescript.utils.d.ts.map +1 -1
- package/dist/core/utils/typescript.utils.js +82 -3
- package/dist/env.d.ts +45 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +51 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/metrics/index.d.ts +1 -1
- package/dist/metrics/index.d.ts.map +1 -1
- package/dist/metrics/index.js +2 -0
- package/dist/metrics/recorder.d.ts.map +1 -1
- package/dist/metrics/types.d.ts +31 -0
- package/dist/metrics/types.d.ts.map +1 -1
- package/dist/metrics/utils/html-report.utils.d.ts +24 -0
- package/dist/metrics/utils/html-report.utils.d.ts.map +1 -0
- package/dist/metrics/utils/html-report.utils.js +1547 -0
- package/dist/metrics/utils/index.d.ts +1 -0
- package/dist/metrics/utils/index.d.ts.map +1 -1
- package/dist/metrics/utils/index.js +2 -0
- package/dist/metrics/utils/writer.utils.d.ts +6 -2
- package/dist/metrics/utils/writer.utils.d.ts.map +1 -1
- package/dist/metrics/utils/writer.utils.js +20 -4
- package/dist/search/generator.d.ts +1 -9
- package/dist/search/generator.d.ts.map +1 -1
- package/dist/search/generator.js +26 -2
- package/dist/seo/generator.d.ts.map +1 -1
- package/dist/seo/generator.js +1 -0
- package/dist/seo/utils/escape-and-validation.utils.d.ts.map +1 -1
- package/dist/seo/utils/escape-and-validation.utils.js +1 -16
- package/dist/types/logging.d.ts +31 -12
- package/dist/types/logging.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { writeFile, readFile, pathExists, remove, ensureDir } from '../utils/index.js';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { hostname } from 'node:os';
|
|
4
|
+
import { unlinkSync, existsSync } from 'node:fs';
|
|
5
|
+
/**
|
|
6
|
+
* Manages dev server locking to prevent multiple dev servers from running in the same directory.
|
|
7
|
+
* Uses a simple file-based locking mechanism with process ID tracking.
|
|
8
|
+
*/
|
|
9
|
+
export class DevServerLockManager {
|
|
10
|
+
lockPath;
|
|
11
|
+
isLocked = false;
|
|
12
|
+
cleanupHandlersRegistered = false;
|
|
13
|
+
constructor(cacheDir) {
|
|
14
|
+
this.lockPath = join(cacheDir, '.dev-server-lock');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Attempts to acquire a dev server lock.
|
|
18
|
+
* Throws an error if another dev server is already running.
|
|
19
|
+
*
|
|
20
|
+
* @throws {Error} When lock cannot be acquired
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const lockManager = new DevServerLockManager('.stati');
|
|
25
|
+
* try {
|
|
26
|
+
* await lockManager.acquireLock();
|
|
27
|
+
* // Start dev server
|
|
28
|
+
* } finally {
|
|
29
|
+
* await lockManager.releaseLock();
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
async acquireLock() {
|
|
34
|
+
try {
|
|
35
|
+
// Check if lock file exists
|
|
36
|
+
if (await pathExists(this.lockPath)) {
|
|
37
|
+
const existingLock = await this.readLockFile();
|
|
38
|
+
if (existingLock) {
|
|
39
|
+
// Check if the process is still running
|
|
40
|
+
if (await this.isProcessRunning(existingLock.pid)) {
|
|
41
|
+
const hostname = existingLock.hostname || 'unknown';
|
|
42
|
+
const isSameHost = hostname === this.getHostname();
|
|
43
|
+
const location = isSameHost ? 'this machine' : `${hostname}`;
|
|
44
|
+
throw new Error(`A dev server is already running in this directory (PID ${existingLock.pid} on ${location}, started ${existingLock.timestamp}).\n` +
|
|
45
|
+
`Please stop the existing dev server before starting a new one.\n` +
|
|
46
|
+
`If you're sure no dev server is running, delete: ${this.lockPath}`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Process is dead, remove stale lock
|
|
50
|
+
console.warn(`Removing stale dev server lock (PID ${existingLock.pid} no longer running)`);
|
|
51
|
+
await this.forceRemoveLock();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Try to create the lock
|
|
56
|
+
await this.createLockFile();
|
|
57
|
+
this.isLocked = true;
|
|
58
|
+
// Register cleanup handlers to ensure lock is released on process exit
|
|
59
|
+
this.registerCleanupHandlers();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const nodeError = error;
|
|
63
|
+
if (nodeError.code === 'EEXIST') {
|
|
64
|
+
throw new Error(`Another dev server is starting in this directory.\n` +
|
|
65
|
+
`Please wait a moment and try again, or delete: ${this.lockPath}`);
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Releases the dev server lock if this process owns it.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* await lockManager.releaseLock();
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
async releaseLock() {
|
|
79
|
+
if (!this.isLocked) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
// Verify we still own the lock before removing it
|
|
84
|
+
const currentLock = await this.readLockFile();
|
|
85
|
+
if (currentLock && currentLock.pid === process.pid) {
|
|
86
|
+
await remove(this.lockPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
// Don't throw on release errors, just warn
|
|
91
|
+
console.warn(`Warning: Failed to release dev server lock: ${error instanceof Error ? error.message : String(error)}`);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
this.isLocked = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Checks if a dev server lock is currently held by any process.
|
|
99
|
+
*
|
|
100
|
+
* @returns True if a lock exists and the owning process is running
|
|
101
|
+
*/
|
|
102
|
+
async isLockHeld() {
|
|
103
|
+
try {
|
|
104
|
+
if (!(await pathExists(this.lockPath))) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const lock = await this.readLockFile();
|
|
108
|
+
if (!lock) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return await this.isProcessRunning(lock.pid);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Gets information about the current lock holder.
|
|
119
|
+
*
|
|
120
|
+
* @returns Lock information or null if no lock exists
|
|
121
|
+
*/
|
|
122
|
+
async getLockInfo() {
|
|
123
|
+
try {
|
|
124
|
+
if (!(await pathExists(this.lockPath))) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return await this.readLockFile();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Force removes the lock file without checking ownership.
|
|
135
|
+
* Should only be used in error recovery scenarios.
|
|
136
|
+
*/
|
|
137
|
+
async forceRemoveLock() {
|
|
138
|
+
try {
|
|
139
|
+
await remove(this.lockPath);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Ignore errors when force removing
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Creates a new lock file with current process information.
|
|
147
|
+
*/
|
|
148
|
+
async createLockFile() {
|
|
149
|
+
const lockInfo = {
|
|
150
|
+
pid: process.pid,
|
|
151
|
+
timestamp: new Date().toISOString(),
|
|
152
|
+
hostname: this.getHostname(),
|
|
153
|
+
};
|
|
154
|
+
// Ensure the cache directory exists before creating the lock file
|
|
155
|
+
await ensureDir(dirname(this.lockPath));
|
|
156
|
+
// Use 'wx' flag to create file exclusively (fails if exists)
|
|
157
|
+
await writeFile(this.lockPath, JSON.stringify(lockInfo, null, 2), { flag: 'wx' });
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Reads and parses the lock file.
|
|
161
|
+
*/
|
|
162
|
+
async readLockFile() {
|
|
163
|
+
try {
|
|
164
|
+
const content = await readFile(this.lockPath, 'utf-8');
|
|
165
|
+
if (!content) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return JSON.parse(content);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Checks if a process with the given PID is currently running.
|
|
176
|
+
*/
|
|
177
|
+
async isProcessRunning(pid) {
|
|
178
|
+
try {
|
|
179
|
+
// On Unix systems, sending signal 0 checks if process exists without affecting it
|
|
180
|
+
process.kill(pid, 0);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
const nodeError = error;
|
|
185
|
+
// ESRCH means process doesn't exist
|
|
186
|
+
return nodeError.code !== 'ESRCH';
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Gets the hostname for lock identification.
|
|
191
|
+
*/
|
|
192
|
+
getHostname() {
|
|
193
|
+
try {
|
|
194
|
+
return hostname();
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return 'unknown';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Registers process exit handlers to ensure lock is released.
|
|
202
|
+
* This prevents accidentally leaving the lock file behind.
|
|
203
|
+
*/
|
|
204
|
+
registerCleanupHandlers() {
|
|
205
|
+
if (this.cleanupHandlersRegistered) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
this.cleanupHandlersRegistered = true;
|
|
209
|
+
const cleanup = async () => {
|
|
210
|
+
await this.releaseLock();
|
|
211
|
+
};
|
|
212
|
+
// Handle various exit scenarios
|
|
213
|
+
process.on('exit', () => {
|
|
214
|
+
// Synchronous cleanup on exit
|
|
215
|
+
if (this.isLocked) {
|
|
216
|
+
try {
|
|
217
|
+
if (existsSync(this.lockPath)) {
|
|
218
|
+
unlinkSync(this.lockPath);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Ignore errors during synchronous cleanup
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
// Handle Ctrl+C
|
|
227
|
+
process.on('SIGINT', async () => {
|
|
228
|
+
await cleanup();
|
|
229
|
+
process.exit(0);
|
|
230
|
+
});
|
|
231
|
+
// Handle kill command
|
|
232
|
+
process.on('SIGTERM', async () => {
|
|
233
|
+
await cleanup();
|
|
234
|
+
process.exit(0);
|
|
235
|
+
});
|
|
236
|
+
// Handle uncaught errors
|
|
237
|
+
process.on('uncaughtException', async (error) => {
|
|
238
|
+
console.error('Uncaught exception:', error);
|
|
239
|
+
await cleanup();
|
|
240
|
+
process.exit(1);
|
|
241
|
+
});
|
|
242
|
+
process.on('unhandledRejection', async (reason) => {
|
|
243
|
+
console.error('Unhandled rejection:', reason);
|
|
244
|
+
await cleanup();
|
|
245
|
+
process.exit(1);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
package/dist/core/isg/hash.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../../src/core/isg/hash.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../../src/core/isg/hash.ts"],"names":[],"mappings":"AA+BA;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAiBhG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA8B9E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAKnF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,MAAM,CAqCnE"}
|
package/dist/core/isg/hash.js
CHANGED
|
@@ -19,6 +19,18 @@ function createSha256Hash(data) {
|
|
|
19
19
|
}
|
|
20
20
|
return `sha256-${hash.digest('hex')}`;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Per-build cache for file hashes to avoid recomputing the same file hash
|
|
24
|
+
* multiple times during a single build.
|
|
25
|
+
* Key: normalized file path, Value: computed hash or null if file doesn't exist
|
|
26
|
+
*/
|
|
27
|
+
const fileHashCache = new Map();
|
|
28
|
+
/**
|
|
29
|
+
* Clears the file hash cache. Should be called at the start of each build.
|
|
30
|
+
*/
|
|
31
|
+
export function clearFileHashCache() {
|
|
32
|
+
fileHashCache.clear();
|
|
33
|
+
}
|
|
22
34
|
/**
|
|
23
35
|
* Computes a SHA-256 hash of page content and front matter.
|
|
24
36
|
* Used to detect when page content has changed.
|
|
@@ -66,18 +78,29 @@ export function computeContentHash(content, frontMatter) {
|
|
|
66
78
|
* ```
|
|
67
79
|
*/
|
|
68
80
|
export async function computeFileHash(filePath) {
|
|
81
|
+
// Normalize path for consistent cache key
|
|
82
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
83
|
+
// Check cache first
|
|
84
|
+
if (fileHashCache.has(normalizedPath)) {
|
|
85
|
+
return fileHashCache.get(normalizedPath) ?? null;
|
|
86
|
+
}
|
|
69
87
|
try {
|
|
70
88
|
if (!(await pathExists(filePath))) {
|
|
89
|
+
fileHashCache.set(normalizedPath, null);
|
|
71
90
|
return null;
|
|
72
91
|
}
|
|
73
92
|
const content = await readFile(filePath, 'utf-8');
|
|
74
93
|
if (!content) {
|
|
94
|
+
fileHashCache.set(normalizedPath, null);
|
|
75
95
|
return null;
|
|
76
96
|
}
|
|
77
|
-
|
|
97
|
+
const hash = createSha256Hash(content);
|
|
98
|
+
fileHashCache.set(normalizedPath, hash);
|
|
99
|
+
return hash;
|
|
78
100
|
}
|
|
79
101
|
catch (error) {
|
|
80
102
|
console.warn(`Failed to compute hash for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
103
|
+
fileHashCache.set(normalizedPath, null);
|
|
81
104
|
return null;
|
|
82
105
|
}
|
|
83
106
|
}
|
package/dist/core/isg/index.d.ts
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
export { loadCacheManifest, saveCacheManifest, createEmptyManifest } from './manifest.js';
|
|
10
10
|
export { shouldRebuildPage, createCacheEntry, updateCacheEntry } from './builder.js';
|
|
11
11
|
export { BuildLockManager, withBuildLock } from './build-lock.js';
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
12
|
+
export { DevServerLockManager } from './dev-server-lock.js';
|
|
13
|
+
export { CircularDependencyError, trackTemplateDependencies, resolveTemplatePath, clearTemplatePathCache, } from './deps.js';
|
|
14
|
+
export { computeContentHash, computeFileHash, computeInputsHash, computeNavigationHash, clearFileHashCache, } from './hash.js';
|
|
14
15
|
export { getSafeCurrentTime, parseSafeDate, computeEffectiveTTL, computeNextRebuildAt, isPageFrozen, applyAgingRules, } from './ttl.js';
|
|
15
16
|
export { ISGConfigurationError, validateISGConfig, validatePageISGOverrides, extractNumericOverride, } from './validation.js';
|
|
16
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/isg/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAG1F,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrF,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/isg/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAG1F,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrF,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,OAAO,EACL,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,eAAe,GAChB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC"}
|
package/dist/core/isg/index.js
CHANGED
|
@@ -12,10 +12,11 @@ export { loadCacheManifest, saveCacheManifest, createEmptyManifest } from './man
|
|
|
12
12
|
export { shouldRebuildPage, createCacheEntry, updateCacheEntry } from './builder.js';
|
|
13
13
|
// Build locking
|
|
14
14
|
export { BuildLockManager, withBuildLock } from './build-lock.js';
|
|
15
|
+
export { DevServerLockManager } from './dev-server-lock.js';
|
|
15
16
|
// Dependency tracking
|
|
16
|
-
export { CircularDependencyError, trackTemplateDependencies, resolveTemplatePath } from './deps.js';
|
|
17
|
+
export { CircularDependencyError, trackTemplateDependencies, resolveTemplatePath, clearTemplatePathCache, } from './deps.js';
|
|
17
18
|
// Hash computation
|
|
18
|
-
export { computeContentHash, computeFileHash, computeInputsHash, computeNavigationHash, } from './hash.js';
|
|
19
|
+
export { computeContentHash, computeFileHash, computeInputsHash, computeNavigationHash, clearFileHashCache, } from './hash.js';
|
|
19
20
|
// TTL and aging
|
|
20
21
|
export { getSafeCurrentTime, parseSafeDate, computeEffectiveTTL, computeNextRebuildAt, isPageFrozen, applyAgingRules, } from './ttl.js';
|
|
21
22
|
// Validation
|
package/dist/core/markdown.d.ts
CHANGED
|
@@ -9,9 +9,15 @@ export interface MarkdownResult {
|
|
|
9
9
|
/** Table of contents entries extracted from headings */
|
|
10
10
|
toc: TocEntry[];
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Clear the cached markdown processor.
|
|
14
|
+
* Call this when the config changes or when starting a fresh build.
|
|
15
|
+
*/
|
|
16
|
+
export declare function clearMarkdownProcessorCache(): void;
|
|
12
17
|
/**
|
|
13
18
|
* Creates and configures a MarkdownIt processor based on the provided configuration.
|
|
14
19
|
* Supports both plugin array format and configure function format.
|
|
20
|
+
* In development mode, caches the processor to avoid recreating it on each rebuild.
|
|
15
21
|
*/
|
|
16
22
|
export declare function createMarkdownProcessor(config: StatiConfig): Promise<MarkdownIt>;
|
|
17
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/core/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAKrC,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/core/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAKrC,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI/D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,GAAG,EAAE,QAAQ,EAAE,CAAC;CACjB;AAQD;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAmBD;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAkDtF;AA6ED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,QAAQ,EAAE,CAGtE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,UAAU,EACd,UAAU,GAAE,OAAc,GACzB,cAAc,CAWhB"}
|
package/dist/core/markdown.js
CHANGED
|
@@ -3,6 +3,19 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { slugify } from './utils/index.js';
|
|
6
|
+
import { isDevelopment } from '../env.js';
|
|
7
|
+
/**
|
|
8
|
+
* Cached markdown processor for dev mode to avoid re-creating on each rebuild.
|
|
9
|
+
* This significantly improves rebuild performance by avoiding repeated plugin loading.
|
|
10
|
+
*/
|
|
11
|
+
let cachedMarkdownProcessor = null;
|
|
12
|
+
/**
|
|
13
|
+
* Clear the cached markdown processor.
|
|
14
|
+
* Call this when the config changes or when starting a fresh build.
|
|
15
|
+
*/
|
|
16
|
+
export function clearMarkdownProcessorCache() {
|
|
17
|
+
cachedMarkdownProcessor = null;
|
|
18
|
+
}
|
|
6
19
|
/**
|
|
7
20
|
* Load a markdown plugin, trying different resolution strategies
|
|
8
21
|
*/
|
|
@@ -22,8 +35,14 @@ async function loadMarkdownPlugin(pluginName) {
|
|
|
22
35
|
/**
|
|
23
36
|
* Creates and configures a MarkdownIt processor based on the provided configuration.
|
|
24
37
|
* Supports both plugin array format and configure function format.
|
|
38
|
+
* In development mode, caches the processor to avoid recreating it on each rebuild.
|
|
25
39
|
*/
|
|
26
40
|
export async function createMarkdownProcessor(config) {
|
|
41
|
+
// In dev mode, return cached processor if available
|
|
42
|
+
// This avoids recreating the processor and reloading plugins on each rebuild
|
|
43
|
+
if (isDevelopment() && cachedMarkdownProcessor) {
|
|
44
|
+
return cachedMarkdownProcessor;
|
|
45
|
+
}
|
|
27
46
|
const md = new MarkdownIt({
|
|
28
47
|
html: true,
|
|
29
48
|
linkify: true,
|
|
@@ -61,6 +80,10 @@ export async function createMarkdownProcessor(config) {
|
|
|
61
80
|
if (config.markdown?.configure) {
|
|
62
81
|
config.markdown.configure(md);
|
|
63
82
|
}
|
|
83
|
+
// Cache in dev mode
|
|
84
|
+
if (isDevelopment()) {
|
|
85
|
+
cachedMarkdownProcessor = md;
|
|
86
|
+
}
|
|
64
87
|
return md;
|
|
65
88
|
}
|
|
66
89
|
/**
|
package/dist/core/preview.js
CHANGED
|
@@ -141,7 +141,7 @@ export async function createPreviewServer(options = {}) {
|
|
|
141
141
|
});
|
|
142
142
|
logger.success?.(`Preview server running at ${url}`);
|
|
143
143
|
logger.info?.(`\nServing:`);
|
|
144
|
-
logger.info?.(`
|
|
144
|
+
logger.info?.(` • ${outDir}`);
|
|
145
145
|
// Open browser if requested
|
|
146
146
|
if (open) {
|
|
147
147
|
try {
|
package/dist/core/templates.js
CHANGED
|
@@ -4,7 +4,7 @@ import glob from 'fast-glob';
|
|
|
4
4
|
import { createFallbackLogger } from './utils/index.js';
|
|
5
5
|
import { TEMPLATE_EXTENSION } from '../constants.js';
|
|
6
6
|
import { getStatiVersion, isCollectionIndexPage, discoverLayout, getCollectionPathForPage, resolveSrcDir, createTemplateError, createValidatingPartialsProxy, propValue, wrapPartialsAsCallable, createNavigationHelpers, } from './utils/index.js';
|
|
7
|
-
import {
|
|
7
|
+
import { isDevelopment, isProduction } from '../env.js';
|
|
8
8
|
import { generateSEO } from '../seo/index.js';
|
|
9
9
|
/**
|
|
10
10
|
* Groups pages by their tags for aggregation purposes.
|
|
@@ -177,8 +177,8 @@ export function createTemplateEngine(config) {
|
|
|
177
177
|
const templateDir = resolveSrcDir(config);
|
|
178
178
|
const eta = new Eta({
|
|
179
179
|
views: templateDir,
|
|
180
|
-
cache:
|
|
181
|
-
cacheFilepaths:
|
|
180
|
+
cache: isProduction(),
|
|
181
|
+
cacheFilepaths: isProduction(),
|
|
182
182
|
varName: 'stati',
|
|
183
183
|
});
|
|
184
184
|
return eta;
|
|
@@ -278,7 +278,7 @@ export async function renderPage(page, body, config, eta, navigation, allPages,
|
|
|
278
278
|
if (pass === maxPasses - 1) {
|
|
279
279
|
log.warning(`Failed to render partial ${partialName} at ${partialPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
280
280
|
// In development mode, throw enhanced template error for partials too
|
|
281
|
-
if (
|
|
281
|
+
if (isDevelopment()) {
|
|
282
282
|
const templateError = createTemplateError(error instanceof Error ? error : new Error(String(error)), partialPath);
|
|
283
283
|
throw templateError;
|
|
284
284
|
}
|
|
@@ -327,7 +327,7 @@ export async function renderPage(page, body, config, eta, navigation, allPages,
|
|
|
327
327
|
catch (error) {
|
|
328
328
|
log.error(`Error rendering layout ${layoutPath || 'unknown'}: ${error instanceof Error ? error.message : String(error)}`);
|
|
329
329
|
// In development mode, throw enhanced template error for better debugging
|
|
330
|
-
if (
|
|
330
|
+
if (isDevelopment()) {
|
|
331
331
|
const templateError = createTemplateError(error instanceof Error ? error : new Error(String(error)), layoutPath || undefined);
|
|
332
332
|
throw templateError;
|
|
333
333
|
}
|
|
@@ -42,6 +42,8 @@ export interface CompiledBundleInfo {
|
|
|
42
42
|
filename: string;
|
|
43
43
|
/** The full path to the bundle (e.g., '/_assets/core-a1b2c3d4.js') */
|
|
44
44
|
path: string;
|
|
45
|
+
/** The size of the compiled bundle in bytes */
|
|
46
|
+
sizeInBytes?: number;
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
49
|
* Determines which bundles should be included on a specific page.
|
|
@@ -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;
|
|
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,CACjC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,YAAY,EAAE,GACtB,YAAY,EAAE,CAyBhB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,kBAAkB,EAAE,GACpC,MAAM,EAAE,CAaV"}
|
|
@@ -22,7 +22,7 @@ export { globToRegex, matchesGlob } from './glob-patterns.utils.js';
|
|
|
22
22
|
export { matchBundlesForPage, getBundlePathsForPage, validateUniqueBundleNames, DuplicateBundleNameError, } from './bundle-matching.utils.js';
|
|
23
23
|
export type { CompiledBundleInfo } from './bundle-matching.utils.js';
|
|
24
24
|
export { createFallbackLogger } from './logger.utils.js';
|
|
25
|
-
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, } from './typescript.utils.js';
|
|
25
|
+
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, } from './typescript.utils.js';
|
|
26
26
|
export type { CompileOptions, WatchOptions } from './typescript.utils.js';
|
|
27
27
|
export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,GACL,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,GACL,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,EACX,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/core/utils/index.js
CHANGED
|
@@ -35,6 +35,6 @@ export { matchBundlesForPage, getBundlePathsForPage, validateUniqueBundleNames,
|
|
|
35
35
|
// Logger utilities
|
|
36
36
|
export { createFallbackLogger } from './logger.utils.js';
|
|
37
37
|
// TypeScript compilation utilities
|
|
38
|
-
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, } from './typescript.utils.js';
|
|
38
|
+
export { compileTypeScript, createTypeScriptWatcher, compileStatiConfig, cleanupCompiledConfig, autoInjectBundles, formatBytes, detectExistingBundles, } from './typescript.utils.js';
|
|
39
39
|
// HTML manipulation utilities
|
|
40
40
|
export { findHeadClosePosition, injectBeforeHeadClose } from './html.utils.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/logger.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"logger.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/logger.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAa7C"}
|
|
@@ -11,6 +11,7 @@ export function createFallbackLogger() {
|
|
|
11
11
|
success: (msg) => console.log(msg),
|
|
12
12
|
error: (msg) => console.error(msg),
|
|
13
13
|
warning: (msg) => console.warn(msg),
|
|
14
|
+
status: (msg) => console.log(msg),
|
|
14
15
|
building: (msg) => console.log(msg),
|
|
15
16
|
processing: (msg) => console.log(msg),
|
|
16
17
|
stats: (msg) => console.log(msg),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isProduction } from '../../env.js';
|
|
2
2
|
/**
|
|
3
3
|
* Creates inline error overlay HTML for missing partials
|
|
4
4
|
*/
|
|
@@ -91,7 +91,7 @@ function findSimilarPartialNames(targetName, availableNames) {
|
|
|
91
91
|
export function createValidatingPartialsProxy(partials) {
|
|
92
92
|
// In production, return partials as-is
|
|
93
93
|
// Only skip validation if explicitly set to production
|
|
94
|
-
if (
|
|
94
|
+
if (isProduction()) {
|
|
95
95
|
return partials;
|
|
96
96
|
}
|
|
97
97
|
// If there are no partials, return the empty object as-is
|
|
@@ -71,6 +71,15 @@ export declare function resolveStaticPath(config: StatiConfig, relativePath: str
|
|
|
71
71
|
* This utility ensures that paths from different sources (file watchers, cache manifest,
|
|
72
72
|
* glob results) can be reliably compared even when they use different representations.
|
|
73
73
|
*
|
|
74
|
+
* **WARNING: This function is for STRING COMPARISON ONLY.**
|
|
75
|
+
* **DO NOT use the returned path for file system operations.**
|
|
76
|
+
*
|
|
77
|
+
* The normalization uppercases Windows drive letters (c: → C:) for consistent
|
|
78
|
+
* comparison on case-insensitive Windows file systems. However, this can break
|
|
79
|
+
* file operations in case-sensitive environments like WSL or Unix systems mounted
|
|
80
|
+
* on Windows. Always use the original path for actual file system operations
|
|
81
|
+
* (fs.readFile, fs.writeFile, etc.).
|
|
82
|
+
*
|
|
74
83
|
* @param filePath - Path to normalize (can be relative or absolute, Windows or POSIX)
|
|
75
84
|
* @param basePath - Optional base path to resolve relative paths against (defaults to cwd)
|
|
76
85
|
* @returns Normalized absolute path with forward slashes and no trailing slashes
|
|
@@ -88,6 +97,15 @@ export declare function resolveStaticPath(config: StatiConfig, relativePath: str
|
|
|
88
97
|
* // Already POSIX path
|
|
89
98
|
* normalizePathForComparison('/project/site/layout.eta')
|
|
90
99
|
* // Returns: '/project/site/layout.eta'
|
|
100
|
+
*
|
|
101
|
+
* // CORRECT: Use for comparison
|
|
102
|
+
* const normalized1 = normalizePathForComparison(watcherPath);
|
|
103
|
+
* const normalized2 = normalizePathForComparison(cachedPath);
|
|
104
|
+
* if (normalized1 === normalized2) { ... }
|
|
105
|
+
*
|
|
106
|
+
* // WRONG: Do not use for file operations
|
|
107
|
+
* const normalized = normalizePathForComparison(filePath);
|
|
108
|
+
* await fs.readFile(normalized); // BAD! Use original filePath instead
|
|
91
109
|
* ```
|
|
92
110
|
*/
|
|
93
111
|
export declare function normalizePathForComparison(filePath: string, basePath?: string): string;
|
|
@@ -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
|
|
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"}
|
|
@@ -90,6 +90,15 @@ export function resolveStaticPath(config, relativePath) {
|
|
|
90
90
|
* This utility ensures that paths from different sources (file watchers, cache manifest,
|
|
91
91
|
* glob results) can be reliably compared even when they use different representations.
|
|
92
92
|
*
|
|
93
|
+
* **WARNING: This function is for STRING COMPARISON ONLY.**
|
|
94
|
+
* **DO NOT use the returned path for file system operations.**
|
|
95
|
+
*
|
|
96
|
+
* The normalization uppercases Windows drive letters (c: → C:) for consistent
|
|
97
|
+
* comparison on case-insensitive Windows file systems. However, this can break
|
|
98
|
+
* file operations in case-sensitive environments like WSL or Unix systems mounted
|
|
99
|
+
* on Windows. Always use the original path for actual file system operations
|
|
100
|
+
* (fs.readFile, fs.writeFile, etc.).
|
|
101
|
+
*
|
|
93
102
|
* @param filePath - Path to normalize (can be relative or absolute, Windows or POSIX)
|
|
94
103
|
* @param basePath - Optional base path to resolve relative paths against (defaults to cwd)
|
|
95
104
|
* @returns Normalized absolute path with forward slashes and no trailing slashes
|
|
@@ -107,6 +116,15 @@ export function resolveStaticPath(config, relativePath) {
|
|
|
107
116
|
* // Already POSIX path
|
|
108
117
|
* normalizePathForComparison('/project/site/layout.eta')
|
|
109
118
|
* // Returns: '/project/site/layout.eta'
|
|
119
|
+
*
|
|
120
|
+
* // CORRECT: Use for comparison
|
|
121
|
+
* const normalized1 = normalizePathForComparison(watcherPath);
|
|
122
|
+
* const normalized2 = normalizePathForComparison(cachedPath);
|
|
123
|
+
* if (normalized1 === normalized2) { ... }
|
|
124
|
+
*
|
|
125
|
+
* // WRONG: Do not use for file operations
|
|
126
|
+
* const normalized = normalizePathForComparison(filePath);
|
|
127
|
+
* await fs.readFile(normalized); // BAD! Use original filePath instead
|
|
110
128
|
* ```
|
|
111
129
|
*/
|
|
112
130
|
export function normalizePathForComparison(filePath, basePath) {
|
|
@@ -120,6 +138,11 @@ export function normalizePathForComparison(filePath, basePath) {
|
|
|
120
138
|
// Use the already-normalized path to avoid reintroducing backslashes
|
|
121
139
|
normalized = join(base, normalized).replace(/\\/g, '/');
|
|
122
140
|
}
|
|
141
|
+
// Normalize Windows drive letter to uppercase for consistent comparison
|
|
142
|
+
// File system is case-insensitive on Windows, but string comparison is case-sensitive
|
|
143
|
+
if (/^[a-z]:/.test(normalized)) {
|
|
144
|
+
normalized = normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
145
|
+
}
|
|
123
146
|
// Use posix.normalize to clean up any '..' or '.' segments and remove redundant separators
|
|
124
147
|
normalized = posix.normalize(normalized);
|
|
125
148
|
// Remove trailing slash (except for root path)
|
|
@@ -72,20 +72,5 @@ export declare function getInventorySize(): number;
|
|
|
72
72
|
* @returns True if tracking is enabled
|
|
73
73
|
*/
|
|
74
74
|
export declare function isTrackingEnabled(): boolean;
|
|
75
|
-
|
|
76
|
-
* Writes the Tailwind class inventory to an HTML file that Tailwind can scan.
|
|
77
|
-
*
|
|
78
|
-
* The generated file contains all tracked classes in a hidden div.
|
|
79
|
-
* This file should be added to Tailwind's content configuration.
|
|
80
|
-
*
|
|
81
|
-
* @param cacheDir - Directory where the inventory file should be written (typically .stati/)
|
|
82
|
-
* @returns Path to the generated inventory file
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```typescript
|
|
86
|
-
* const inventoryPath = await writeTailwindClassInventory('/path/to/project/.stati');
|
|
87
|
-
* // File written to: /path/to/project/.stati/tailwind-classes.html
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
export declare function writeTailwindClassInventory(cacheDir: string): Promise<string>;
|
|
75
|
+
export declare function writeTailwindClassInventory(cacheDir: string, skipIfUnchanged?: boolean): Promise<string>;
|
|
91
76
|
//# sourceMappingURL=tailwind-inventory.utils.d.ts.map
|