@stati/core 1.21.0 → 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.
Files changed (65) hide show
  1. package/dist/config/loader.d.ts +4 -0
  2. package/dist/config/loader.d.ts.map +1 -1
  3. package/dist/config/loader.js +49 -4
  4. package/dist/core/build.d.ts +6 -0
  5. package/dist/core/build.d.ts.map +1 -1
  6. package/dist/core/build.js +134 -24
  7. package/dist/core/dev.d.ts.map +1 -1
  8. package/dist/core/dev.js +86 -19
  9. package/dist/core/isg/builder.d.ts +4 -1
  10. package/dist/core/isg/builder.d.ts.map +1 -1
  11. package/dist/core/isg/builder.js +89 -2
  12. package/dist/core/isg/deps.d.ts +5 -0
  13. package/dist/core/isg/deps.d.ts.map +1 -1
  14. package/dist/core/isg/deps.js +38 -3
  15. package/dist/core/isg/dev-server-lock.d.ts +85 -0
  16. package/dist/core/isg/dev-server-lock.d.ts.map +1 -0
  17. package/dist/core/isg/dev-server-lock.js +248 -0
  18. package/dist/core/isg/hash.d.ts +4 -0
  19. package/dist/core/isg/hash.d.ts.map +1 -1
  20. package/dist/core/isg/hash.js +24 -1
  21. package/dist/core/isg/index.d.ts +3 -2
  22. package/dist/core/isg/index.d.ts.map +1 -1
  23. package/dist/core/isg/index.js +3 -2
  24. package/dist/core/markdown.d.ts +6 -0
  25. package/dist/core/markdown.d.ts.map +1 -1
  26. package/dist/core/markdown.js +23 -0
  27. package/dist/core/templates.js +5 -5
  28. package/dist/core/utils/index.d.ts +1 -1
  29. package/dist/core/utils/index.d.ts.map +1 -1
  30. package/dist/core/utils/index.js +1 -1
  31. package/dist/core/utils/partial-validation.utils.js +2 -2
  32. package/dist/core/utils/paths.utils.d.ts +18 -0
  33. package/dist/core/utils/paths.utils.d.ts.map +1 -1
  34. package/dist/core/utils/paths.utils.js +23 -0
  35. package/dist/core/utils/tailwind-inventory.utils.d.ts +1 -16
  36. package/dist/core/utils/tailwind-inventory.utils.d.ts.map +1 -1
  37. package/dist/core/utils/tailwind-inventory.utils.js +35 -3
  38. package/dist/core/utils/typescript.utils.d.ts +9 -0
  39. package/dist/core/utils/typescript.utils.d.ts.map +1 -1
  40. package/dist/core/utils/typescript.utils.js +41 -0
  41. package/dist/env.d.ts +45 -0
  42. package/dist/env.d.ts.map +1 -1
  43. package/dist/env.js +51 -0
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +2 -2
  47. package/dist/metrics/index.d.ts +1 -1
  48. package/dist/metrics/index.d.ts.map +1 -1
  49. package/dist/metrics/index.js +2 -0
  50. package/dist/metrics/recorder.d.ts.map +1 -1
  51. package/dist/metrics/types.d.ts +31 -0
  52. package/dist/metrics/types.d.ts.map +1 -1
  53. package/dist/metrics/utils/html-report.utils.d.ts +24 -0
  54. package/dist/metrics/utils/html-report.utils.d.ts.map +1 -0
  55. package/dist/metrics/utils/html-report.utils.js +1547 -0
  56. package/dist/metrics/utils/index.d.ts +1 -0
  57. package/dist/metrics/utils/index.d.ts.map +1 -1
  58. package/dist/metrics/utils/index.js +2 -0
  59. package/dist/metrics/utils/writer.utils.d.ts +6 -2
  60. package/dist/metrics/utils/writer.utils.d.ts.map +1 -1
  61. package/dist/metrics/utils/writer.utils.js +20 -4
  62. package/dist/search/generator.d.ts +1 -9
  63. package/dist/search/generator.d.ts.map +1 -1
  64. package/dist/search/generator.js +26 -2
  65. 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
+ }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Clears the file hash cache. Should be called at the start of each build.
3
+ */
4
+ export declare function clearFileHashCache(): void;
1
5
  /**
2
6
  * Computes a SHA-256 hash of page content and front matter.
3
7
  * Used to detect when page content has changed.
@@ -1 +1 @@
1
- {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../../src/core/isg/hash.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;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,CAiB9E;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"}
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"}
@@ -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
- return createSha256Hash(content);
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
  }
@@ -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 { CircularDependencyError, trackTemplateDependencies, resolveTemplatePath } from './deps.js';
13
- export { computeContentHash, computeFileHash, computeInputsHash, computeNavigationHash, } from './hash.js';
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;AAGlE,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAGpG,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,GACtB,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"}
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"}
@@ -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
@@ -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;AAG/D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,GAAG,EAAE,QAAQ,EAAE,CAAC;CACjB;AAmBD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAuCtF;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"}
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"}
@@ -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
  /**
@@ -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 { getEnv } from '../env.js';
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: getEnv() === 'production',
181
- cacheFilepaths: getEnv() === 'production',
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 (getEnv() === 'development') {
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 (getEnv() === 'development') {
330
+ if (isDevelopment()) {
331
331
  const templateError = createTemplateError(error instanceof Error ? error : new Error(String(error)), layoutPath || undefined);
332
332
  throw templateError;
333
333
  }
@@ -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, formatBytes, } 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,EACjB,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,GACL,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,2BAA2B,EAC3B,cAAc,EACd,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAG9E,OAAO,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"}
@@ -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, formatBytes, } 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,4 +1,4 @@
1
- import { getEnv } from '../../env.js';
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 (getEnv() === 'production') {
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;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAuBtF;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"}
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"tailwind-inventory.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/tailwind-inventory.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAsBH;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4C1F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC7E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CnF"}
1
+ {"version":3,"file":"tailwind-inventory.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/tailwind-inventory.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4C1F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC7E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAoCD,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,EAChB,eAAe,UAAQ,GACtB,OAAO,CAAC,MAAM,CAAC,CA8DjB"}