@stati/core 1.15.2 → 1.16.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/core/build.js
CHANGED
|
@@ -58,7 +58,7 @@ async function copyStaticAssetsWithLogging(sourceDir, destDir, logger, basePath
|
|
|
58
58
|
if (item.isDirectory()) {
|
|
59
59
|
// Recursively copy directories
|
|
60
60
|
await ensureDir(destPath);
|
|
61
|
-
filesCopied += await copyStaticAssetsWithLogging(sourcePath, destPath, logger
|
|
61
|
+
filesCopied += await copyStaticAssetsWithLogging(sourcePath, destPath, logger);
|
|
62
62
|
}
|
|
63
63
|
else {
|
|
64
64
|
// Copy individual files
|
package/dist/core/dev.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/core/dev.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA2B7D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb;
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/core/dev.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA2B7D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb;AAiXD,wBAAsB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,SAAS,CAAC,CAudxF"}
|
package/dist/core/dev.js
CHANGED
|
@@ -61,15 +61,32 @@ async function performInitialBuild(configPath, logger, onError) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
* Performs incremental rebuild when files change, using ISG logic for smart rebuilds
|
|
64
|
+
* Performs incremental rebuild when files change, using ISG logic for smart rebuilds.
|
|
65
|
+
* Uses a pending changes queue to ensure no file changes are lost when builds are in progress.
|
|
66
|
+
*
|
|
67
|
+
* @param changedPath - The path of the file that changed
|
|
68
|
+
* @param eventType - The type of change: 'add', 'change', or 'unlink'
|
|
69
|
+
* @param configPath - Optional path to the stati config file
|
|
70
|
+
* @param staticDir - The static assets directory path
|
|
71
|
+
* @param logger - Logger instance for output
|
|
72
|
+
* @param wsServer - WebSocket server for live reload notifications
|
|
73
|
+
* @param isBuildingRef - Mutable reference tracking whether a build is currently in progress
|
|
74
|
+
* @param pendingChangesRef - Mutable reference holding queued file changes that occurred during an
|
|
75
|
+
* active build. The `changes` Map stores file paths as keys and their event types as values,
|
|
76
|
+
* ensuring each file is only processed once with its most recent event type.
|
|
77
|
+
* @param onError - Optional callback invoked when build errors occur
|
|
78
|
+
* @param batchedChanges - Array of changes to process in a single batch (used for pending queue processing)
|
|
65
79
|
*/
|
|
66
|
-
async function performIncrementalRebuild(changedPath, configPath, logger, wsServer, isBuildingRef, onError) {
|
|
80
|
+
async function performIncrementalRebuild(changedPath, eventType, configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, onError, batchedChanges = []) {
|
|
81
|
+
// If a build is in progress, queue this change for processing after the current build
|
|
67
82
|
if (isBuildingRef.value) {
|
|
68
|
-
|
|
83
|
+
pendingChangesRef.changes.set(changedPath, eventType);
|
|
69
84
|
return;
|
|
70
85
|
}
|
|
71
86
|
isBuildingRef.value = true;
|
|
72
87
|
const startTime = Date.now();
|
|
88
|
+
// All changes being processed in this build (primary + batched)
|
|
89
|
+
const allChanges = [{ path: changedPath, eventType }, ...batchedChanges];
|
|
73
90
|
// Create a quiet logger for dev builds that suppresses verbose output
|
|
74
91
|
const devLogger = {
|
|
75
92
|
info: () => { }, // Suppress info messages
|
|
@@ -80,11 +97,15 @@ async function performIncrementalRebuild(changedPath, configPath, logger, wsServ
|
|
|
80
97
|
processing: () => { }, // Suppress processing messages
|
|
81
98
|
stats: () => { }, // Suppress stats messages
|
|
82
99
|
};
|
|
100
|
+
// Helper to check if a path is a static asset
|
|
101
|
+
const normalizedStaticDir = staticDir.replace(/\\/g, '/');
|
|
102
|
+
const isStaticAsset = (path) => {
|
|
103
|
+
const normalizedPath = path.replace(/\\/g, '/');
|
|
104
|
+
return normalizedPath.startsWith(normalizedStaticDir);
|
|
105
|
+
};
|
|
106
|
+
// Helper to get relative path
|
|
107
|
+
const getRelativePath = (path) => path.replace(process.cwd(), '').replace(/\\/g, '/').replace(/^\//, '');
|
|
83
108
|
try {
|
|
84
|
-
const relativePath = changedPath
|
|
85
|
-
.replace(process.cwd(), '')
|
|
86
|
-
.replace(/\\/g, '/')
|
|
87
|
-
.replace(/^\//, '');
|
|
88
109
|
// Check if the changed file is a template/partial
|
|
89
110
|
if (changedPath.endsWith(TEMPLATE_EXTENSION) || changedPath.includes('_partials')) {
|
|
90
111
|
await handleTemplateChange(changedPath, configPath, devLogger);
|
|
@@ -116,7 +137,22 @@ async function performIncrementalRebuild(changedPath, configPath, logger, wsServ
|
|
|
116
137
|
});
|
|
117
138
|
}
|
|
118
139
|
const duration = Date.now() - startTime;
|
|
119
|
-
|
|
140
|
+
// Log all files that were processed in this batch
|
|
141
|
+
for (const change of allChanges) {
|
|
142
|
+
const relativePath = getRelativePath(change.path);
|
|
143
|
+
let action;
|
|
144
|
+
if (change.eventType === 'unlink') {
|
|
145
|
+
action = 'deleted';
|
|
146
|
+
}
|
|
147
|
+
else if (isStaticAsset(change.path)) {
|
|
148
|
+
action = 'copied';
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
action = 'rebuilt';
|
|
152
|
+
}
|
|
153
|
+
logger.info?.(`⚡ ${relativePath} ${action}`);
|
|
154
|
+
}
|
|
155
|
+
logger.info?.(` Done in ${duration}ms`);
|
|
120
156
|
}
|
|
121
157
|
catch (error) {
|
|
122
158
|
const buildError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -129,6 +165,18 @@ async function performIncrementalRebuild(changedPath, configPath, logger, wsServ
|
|
|
129
165
|
}
|
|
130
166
|
finally {
|
|
131
167
|
isBuildingRef.value = false;
|
|
168
|
+
// Check if there are pending changes that occurred during this build
|
|
169
|
+
if (pendingChangesRef.changes.size > 0) {
|
|
170
|
+
// Get all pending changes - the build will process all of them at once
|
|
171
|
+
const pendingChanges = Array.from(pendingChangesRef.changes.entries()).map(([path, evtType]) => ({ path, eventType: evtType }));
|
|
172
|
+
pendingChangesRef.changes.clear();
|
|
173
|
+
// Trigger another rebuild with all batched changes
|
|
174
|
+
// The build() call will copy all static assets and rebuild all affected pages
|
|
175
|
+
const [first, ...remaining] = pendingChanges;
|
|
176
|
+
if (first) {
|
|
177
|
+
void performIncrementalRebuild(first.path, first.eventType, configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, onError, remaining);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
132
180
|
}
|
|
133
181
|
}
|
|
134
182
|
/**
|
|
@@ -291,8 +339,10 @@ export async function createDevServer(options = {}) {
|
|
|
291
339
|
lastBuildError = error;
|
|
292
340
|
};
|
|
293
341
|
let watcher = null;
|
|
342
|
+
let cssWatcher = null;
|
|
294
343
|
let tsWatchers = [];
|
|
295
344
|
const isBuildingRef = { value: false };
|
|
345
|
+
const pendingChangesRef = { changes: new Map() };
|
|
296
346
|
let isStopping = false;
|
|
297
347
|
/**
|
|
298
348
|
* Gets MIME type for a file based on its extension
|
|
@@ -564,14 +614,34 @@ export async function createDevServer(options = {}) {
|
|
|
564
614
|
persistent: true,
|
|
565
615
|
ignoreInitial: true,
|
|
566
616
|
});
|
|
617
|
+
// Set up separate watcher for CSS files in output directory
|
|
618
|
+
// This enables live reload when external tools (like Tailwind CLI) update CSS
|
|
619
|
+
cssWatcher = chokidar.watch([join(outDir, '**/*.css')], {
|
|
620
|
+
ignored: /(^|[/\\])\../, // ignore dotfiles
|
|
621
|
+
persistent: true,
|
|
622
|
+
ignoreInitial: true,
|
|
623
|
+
});
|
|
624
|
+
cssWatcher.on('change', (path) => {
|
|
625
|
+
const relativePath = path.replace(process.cwd(), '').replace(/\\/g, '/').replace(/^\//, '');
|
|
626
|
+
logger.info?.(`⚡ ${relativePath} updated`);
|
|
627
|
+
// Just notify clients to reload - no rebuild needed since CSS was already compiled
|
|
628
|
+
if (wsServer) {
|
|
629
|
+
wsServer.clients.forEach((client) => {
|
|
630
|
+
const ws = client;
|
|
631
|
+
if (ws.readyState === 1) {
|
|
632
|
+
ws.send(JSON.stringify({ type: 'reload' }));
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
});
|
|
567
637
|
watcher.on('change', (path) => {
|
|
568
|
-
void performIncrementalRebuild(path, configPath, logger, wsServer, isBuildingRef, setLastBuildError);
|
|
638
|
+
void performIncrementalRebuild(path, 'change', configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, setLastBuildError);
|
|
569
639
|
});
|
|
570
640
|
watcher.on('add', (path) => {
|
|
571
|
-
void performIncrementalRebuild(path, configPath, logger, wsServer, isBuildingRef, setLastBuildError);
|
|
641
|
+
void performIncrementalRebuild(path, 'add', configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, setLastBuildError);
|
|
572
642
|
});
|
|
573
643
|
watcher.on('unlink', (path) => {
|
|
574
|
-
void performIncrementalRebuild(path, configPath, logger, wsServer, isBuildingRef, setLastBuildError);
|
|
644
|
+
void performIncrementalRebuild(path, 'unlink', configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, setLastBuildError);
|
|
575
645
|
});
|
|
576
646
|
logger.success?.(`Dev server running at ${url}`);
|
|
577
647
|
logger.info?.(`\nServing:`);
|
|
@@ -598,6 +668,11 @@ export async function createDevServer(options = {}) {
|
|
|
598
668
|
await watcher.close();
|
|
599
669
|
watcher = null;
|
|
600
670
|
}
|
|
671
|
+
// Clean up CSS watcher
|
|
672
|
+
if (cssWatcher) {
|
|
673
|
+
await cssWatcher.close();
|
|
674
|
+
cssWatcher = null;
|
|
675
|
+
}
|
|
601
676
|
// Clean up TypeScript watchers
|
|
602
677
|
if (tsWatchers.length > 0) {
|
|
603
678
|
await Promise.all(tsWatchers.map(async (w) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callable-partials.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/callable-partials.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAC1C,QAAQ,IAAI,MAAM,CAAC;IACnB,OAAO,IAAI,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,eAAe,EAAE,MAAM,GACtB,eAAe,CAqDjB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"callable-partials.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/callable-partials.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAC1C,QAAQ,IAAI,MAAM,CAAC;IACnB,OAAO,IAAI,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,eAAe,EAAE,MAAM,GACtB,eAAe,CAqDjB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CA8BjC"}
|
|
@@ -96,13 +96,23 @@ export function makeCallablePartial(eta, partialPath, baseContext, renderedConte
|
|
|
96
96
|
*/
|
|
97
97
|
export function wrapPartialsAsCallable(eta, partials, partialPaths, baseContext) {
|
|
98
98
|
const callablePartials = {};
|
|
99
|
+
// Create a context that will have partials updated to the callable versions
|
|
100
|
+
// This enables nested callable partial calls
|
|
101
|
+
const contextWithCallablePartials = {
|
|
102
|
+
...baseContext,
|
|
103
|
+
// The partials property will be updated after all callables are created
|
|
104
|
+
get partials() {
|
|
105
|
+
return callablePartials;
|
|
106
|
+
},
|
|
107
|
+
};
|
|
99
108
|
for (const [name, renderedContent] of Object.entries(partials)) {
|
|
100
109
|
const partialPath = partialPaths[name];
|
|
101
110
|
if (!partialPath) {
|
|
102
111
|
console.warn(`No path found for partial "${name}", skipping callable wrapper`);
|
|
103
112
|
continue;
|
|
104
113
|
}
|
|
105
|
-
|
|
114
|
+
// Pass the context that has dynamic access to all callable partials
|
|
115
|
+
callablePartials[name] = makeCallablePartial(eta, partialPath, contextWithCallablePartials, renderedContent);
|
|
106
116
|
}
|
|
107
117
|
return callablePartials;
|
|
108
118
|
}
|