@stati/core 1.23.4 → 1.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/build.js +13 -0
- package/dist/core/dev.d.ts.map +1 -1
- package/dist/core/dev.js +79 -62
- package/dist/core/utils/dev-css-watcher.utils.d.ts +26 -0
- package/dist/core/utils/dev-css-watcher.utils.d.ts.map +1 -0
- package/dist/core/utils/dev-css-watcher.utils.js +70 -0
- package/dist/core/utils/env.utils.d.ts +5 -0
- package/dist/core/utils/env.utils.d.ts.map +1 -0
- package/dist/core/utils/env.utils.js +8 -0
- package/dist/core/utils/index.d.ts +3 -0
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core/utils/index.js +4 -0
- package/dist/core/utils/typescript.utils.d.ts +5 -0
- package/dist/core/utils/typescript.utils.d.ts.map +1 -1
- package/dist/core/utils/typescript.utils.js +20 -6
- package/package.json +1 -1
package/dist/core/build.js
CHANGED
|
@@ -551,6 +551,19 @@ async function buildInternal(options = {}) {
|
|
|
551
551
|
outDir: config.outDir || DEFAULT_OUT_DIR,
|
|
552
552
|
mode: 'development',
|
|
553
553
|
});
|
|
554
|
+
// On initial dev startup, watcher output may not exist yet.
|
|
555
|
+
// Compile once so pages can auto-inject bundle scripts immediately.
|
|
556
|
+
if (compiledBundles.length === 0) {
|
|
557
|
+
const endTsSpan = recorder.startSpan('typescriptCompileMs');
|
|
558
|
+
compiledBundles = await compileTypeScript({
|
|
559
|
+
projectRoot: process.cwd(),
|
|
560
|
+
config: config.typescript,
|
|
561
|
+
outDir: config.outDir || DEFAULT_OUT_DIR,
|
|
562
|
+
mode: 'development',
|
|
563
|
+
logger,
|
|
564
|
+
});
|
|
565
|
+
endTsSpan();
|
|
566
|
+
}
|
|
554
567
|
}
|
|
555
568
|
else if (config.typescript?.enabled) {
|
|
556
569
|
const endTsSpan = recorder.startSpan('typescriptCompileMs');
|
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;
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/core/dev.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAsC7D,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;AAscD,wBAAsB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,SAAS,CAAC,CAihBxF"}
|
package/dist/core/dev.js
CHANGED
|
@@ -11,7 +11,7 @@ import { clearMarkdownProcessorCache } from './markdown.js';
|
|
|
11
11
|
import { loadCacheManifest, saveCacheManifest, computeNavigationHash, DevServerLockManager, } from './isg/index.js';
|
|
12
12
|
import { loadContent } from './content.js';
|
|
13
13
|
import { buildNavigation } from './navigation.js';
|
|
14
|
-
import { resolveDevPaths, resolveCacheDir, resolvePrettyUrl, createErrorOverlay, parseErrorDetails, TemplateError, createFallbackLogger, mergeServerOptions, createTypeScriptWatcher, normalizePathForComparison, isPathWithinDirectory, } from './utils/index.js';
|
|
14
|
+
import { resolveDevPaths, resolveCacheDir, resolvePrettyUrl, createErrorOverlay, parseErrorDetails, TemplateError, createFallbackLogger, mergeServerOptions, createTypeScriptWatcher, isEnabledEnvFlag, refreshCssOutputWatcher, normalizePathForComparison, isPathWithinDirectory, } from './utils/index.js';
|
|
15
15
|
import { setEnv, getEnv } from '../env.js';
|
|
16
16
|
import { DEFAULT_DEV_PORT, DEFAULT_DEV_HOST, TEMPLATE_EXTENSION, DEFAULT_OUT_DIR, } from '../constants.js';
|
|
17
17
|
/**
|
|
@@ -77,9 +77,10 @@ async function performInitialBuild(configPath, logger, onError) {
|
|
|
77
77
|
* active build. The `changes` Map stores file paths as keys and their event types as values,
|
|
78
78
|
* ensuring each file is only processed once with its most recent event type.
|
|
79
79
|
* @param onError - Optional callback invoked when build errors occur
|
|
80
|
+
* @param onBuildEnd - Optional callback invoked after each build attempt finishes (success or failure)
|
|
80
81
|
* @param batchedChanges - Array of changes to process in a single batch (used for pending queue processing)
|
|
81
82
|
*/
|
|
82
|
-
async function performIncrementalRebuild(changedPath, eventType, configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, onError, batchedChanges = []) {
|
|
83
|
+
async function performIncrementalRebuild(changedPath, eventType, configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, onError, onBuildEnd, batchedChanges = []) {
|
|
83
84
|
// If a build is in progress, queue this change for processing after the current build
|
|
84
85
|
if (isBuildingRef.value) {
|
|
85
86
|
pendingChangesRef.changes.set(changedPath, eventType);
|
|
@@ -195,6 +196,14 @@ async function performIncrementalRebuild(changedPath, eventType, configPath, sta
|
|
|
195
196
|
}
|
|
196
197
|
finally {
|
|
197
198
|
isBuildingRef.value = false;
|
|
199
|
+
if (onBuildEnd) {
|
|
200
|
+
try {
|
|
201
|
+
await onBuildEnd();
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
logger.error?.(`Failed post-build hook after rebuild: ${error instanceof Error ? error.message : String(error)}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
198
207
|
// Check if there are pending changes that occurred during this build
|
|
199
208
|
if (pendingChangesRef.changes.size > 0) {
|
|
200
209
|
// Get all pending changes - the build will process all of them at once
|
|
@@ -204,7 +213,7 @@ async function performIncrementalRebuild(changedPath, eventType, configPath, sta
|
|
|
204
213
|
// The build() call will copy all static assets and rebuild all affected pages
|
|
205
214
|
const [first, ...remaining] = pendingChanges;
|
|
206
215
|
if (first) {
|
|
207
|
-
void performIncrementalRebuild(first.path, first.eventType, configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, onError, remaining);
|
|
216
|
+
void performIncrementalRebuild(first.path, first.eventType, configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, onError, onBuildEnd, remaining);
|
|
208
217
|
}
|
|
209
218
|
}
|
|
210
219
|
}
|
|
@@ -405,6 +414,10 @@ export async function createDevServer(options = {}) {
|
|
|
405
414
|
const isBuildingRef = { value: false };
|
|
406
415
|
const pendingChangesRef = { changes: new Map() };
|
|
407
416
|
let isStopping = false;
|
|
417
|
+
const watchedCssFiles = new Set();
|
|
418
|
+
// Diagnostics toggles used for investigating dev-server rebuild slowdown.
|
|
419
|
+
const disableCssWatcher = isEnabledEnvFlag(process.env.STATI_DEV_DISABLE_CSS_WATCHER);
|
|
420
|
+
const disableWsReload = isEnabledEnvFlag(process.env.STATI_DEV_DISABLE_WS_RELOAD);
|
|
408
421
|
/**
|
|
409
422
|
* Gets MIME type for a file based on its extension
|
|
410
423
|
*/
|
|
@@ -594,6 +607,45 @@ export async function createDevServer(options = {}) {
|
|
|
594
607
|
logger.error?.(`Failed to start dev server:\n${message}`);
|
|
595
608
|
throw error;
|
|
596
609
|
}
|
|
610
|
+
// TypeScript watcher setup
|
|
611
|
+
// Start this before the initial build so bundle outputs already exist for auto-injection.
|
|
612
|
+
if (config.typescript?.enabled) {
|
|
613
|
+
try {
|
|
614
|
+
// Start TypeScript watchers for hot reload (one per bundle)
|
|
615
|
+
tsWatchers = await createTypeScriptWatcher({
|
|
616
|
+
projectRoot: process.cwd(),
|
|
617
|
+
config: config.typescript,
|
|
618
|
+
outDir: config.outDir || DEFAULT_OUT_DIR,
|
|
619
|
+
logger,
|
|
620
|
+
awaitInitialBuild: true,
|
|
621
|
+
onRebuild: (_results, compileTimeMs) => {
|
|
622
|
+
logger.info?.(`▸ TypeScript recompiled in ${compileTimeMs}ms`);
|
|
623
|
+
// Broadcast reload to WebSocket clients
|
|
624
|
+
if (wsServer && !disableWsReload) {
|
|
625
|
+
wsServer.clients.forEach((client) => {
|
|
626
|
+
const ws = client;
|
|
627
|
+
if (ws.readyState === 1) {
|
|
628
|
+
// WebSocket.OPEN
|
|
629
|
+
ws.send(JSON.stringify({ type: 'reload' }));
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
catch (error) {
|
|
637
|
+
const tsError = error instanceof Error ? error : new Error(String(error));
|
|
638
|
+
// Prominent notification for TypeScript setup failure
|
|
639
|
+
console.log();
|
|
640
|
+
logger.error?.(`TypeScript setup failed: ${tsError.message}`);
|
|
641
|
+
logger.warning?.('──────────────────────────────────────────────────────────────');
|
|
642
|
+
logger.warning?.('! TypeScript hot reload is DISABLED for this session.');
|
|
643
|
+
logger.warning?.(" Dev server will continue, but TypeScript changes won't auto-reload.");
|
|
644
|
+
logger.warning?.(' Fix your TypeScript configuration and restart the dev server.');
|
|
645
|
+
logger.warning?.('──────────────────────────────────────────────────────────────');
|
|
646
|
+
console.log();
|
|
647
|
+
}
|
|
648
|
+
}
|
|
597
649
|
// Perform initial build
|
|
598
650
|
await performInitialBuild(configPath, logger, setLastBuildError);
|
|
599
651
|
// Create HTTP server
|
|
@@ -661,43 +713,6 @@ export async function createDevServer(options = {}) {
|
|
|
661
713
|
reject(error);
|
|
662
714
|
});
|
|
663
715
|
});
|
|
664
|
-
// TypeScript watcher setup (initial compilation is handled by performInitialBuild)
|
|
665
|
-
if (config.typescript?.enabled) {
|
|
666
|
-
try {
|
|
667
|
-
// Start TypeScript watchers for hot reload (one per bundle)
|
|
668
|
-
tsWatchers = await createTypeScriptWatcher({
|
|
669
|
-
projectRoot: process.cwd(),
|
|
670
|
-
config: config.typescript,
|
|
671
|
-
outDir: config.outDir || DEFAULT_OUT_DIR,
|
|
672
|
-
logger,
|
|
673
|
-
onRebuild: (_results, compileTimeMs) => {
|
|
674
|
-
logger.info?.(`▸ TypeScript recompiled in ${compileTimeMs}ms`);
|
|
675
|
-
// Broadcast reload to WebSocket clients
|
|
676
|
-
if (wsServer) {
|
|
677
|
-
wsServer.clients.forEach((client) => {
|
|
678
|
-
const ws = client;
|
|
679
|
-
if (ws.readyState === 1) {
|
|
680
|
-
// WebSocket.OPEN
|
|
681
|
-
ws.send(JSON.stringify({ type: 'reload' }));
|
|
682
|
-
}
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
catch (error) {
|
|
689
|
-
const tsError = error instanceof Error ? error : new Error(String(error));
|
|
690
|
-
// Prominent notification for TypeScript setup failure
|
|
691
|
-
console.log();
|
|
692
|
-
logger.error?.(`TypeScript setup failed: ${tsError.message}`);
|
|
693
|
-
logger.warning?.('──────────────────────────────────────────────────────────────');
|
|
694
|
-
logger.warning?.('! TypeScript hot reload is DISABLED for this session.');
|
|
695
|
-
logger.warning?.(" Dev server will continue, but TypeScript changes won't auto-reload.");
|
|
696
|
-
logger.warning?.(' Fix your TypeScript configuration and restart the dev server.');
|
|
697
|
-
logger.warning?.('──────────────────────────────────────────────────────────────');
|
|
698
|
-
console.log();
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
716
|
// Set up file watching
|
|
702
717
|
const watchPaths = [srcDir, staticDir].filter(Boolean);
|
|
703
718
|
watcher = chokidar.watch(watchPaths, {
|
|
@@ -705,40 +720,42 @@ export async function createDevServer(options = {}) {
|
|
|
705
720
|
persistent: true,
|
|
706
721
|
ignoreInitial: true,
|
|
707
722
|
});
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (ws.readyState === 1) {
|
|
723
|
-
ws.send(JSON.stringify({ type: 'reload' }));
|
|
724
|
-
}
|
|
725
|
-
});
|
|
723
|
+
const refreshCssWatcher = async () => {
|
|
724
|
+
cssWatcher = await refreshCssOutputWatcher({
|
|
725
|
+
outDir,
|
|
726
|
+
logger,
|
|
727
|
+
wsServer,
|
|
728
|
+
disableWsReload,
|
|
729
|
+
cssWatcher,
|
|
730
|
+
watchedCssFiles,
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
if (!disableCssWatcher) {
|
|
734
|
+
await refreshCssWatcher();
|
|
735
|
+
if (!cssWatcher) {
|
|
736
|
+
logger.info?.(' • [dev] No CSS files found in output; CSS watcher not started');
|
|
726
737
|
}
|
|
727
|
-
}
|
|
738
|
+
}
|
|
728
739
|
watcher.on('change', (path) => {
|
|
729
|
-
void performIncrementalRebuild(path, 'change', configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, setLastBuildError);
|
|
740
|
+
void performIncrementalRebuild(path, 'change', configPath, staticDir, logger, disableWsReload ? null : wsServer, isBuildingRef, pendingChangesRef, setLastBuildError, disableCssWatcher ? undefined : refreshCssWatcher);
|
|
730
741
|
});
|
|
731
742
|
watcher.on('add', (path) => {
|
|
732
|
-
void performIncrementalRebuild(path, 'add', configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, setLastBuildError);
|
|
743
|
+
void performIncrementalRebuild(path, 'add', configPath, staticDir, logger, disableWsReload ? null : wsServer, isBuildingRef, pendingChangesRef, setLastBuildError, disableCssWatcher ? undefined : refreshCssWatcher);
|
|
733
744
|
});
|
|
734
745
|
watcher.on('unlink', (path) => {
|
|
735
|
-
void performIncrementalRebuild(path, 'unlink', configPath, staticDir, logger, wsServer, isBuildingRef, pendingChangesRef, setLastBuildError);
|
|
746
|
+
void performIncrementalRebuild(path, 'unlink', configPath, staticDir, logger, disableWsReload ? null : wsServer, isBuildingRef, pendingChangesRef, setLastBuildError, disableCssWatcher ? undefined : refreshCssWatcher);
|
|
736
747
|
});
|
|
737
748
|
logger.success?.(`Dev server running at ${url}`);
|
|
738
749
|
logger.info?.(`\nServing:`);
|
|
739
750
|
logger.info?.(` • ${outDir}`);
|
|
740
751
|
logger.info?.('Watching:');
|
|
741
752
|
watchPaths.forEach((path) => logger.info?.(` • ${path}`));
|
|
753
|
+
if (disableCssWatcher) {
|
|
754
|
+
logger.info?.(' • [diagnostic] CSS output watcher disabled');
|
|
755
|
+
}
|
|
756
|
+
if (disableWsReload) {
|
|
757
|
+
logger.info?.(' • [diagnostic] WebSocket reload broadcast disabled');
|
|
758
|
+
}
|
|
742
759
|
logger.info?.('');
|
|
743
760
|
// Open browser if requested
|
|
744
761
|
if (open) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FSWatcher } from 'chokidar';
|
|
2
|
+
import type { WebSocketServer } from 'ws';
|
|
3
|
+
import type { Logger } from '../../types/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Returns concrete CSS files currently present in outDir.
|
|
6
|
+
*
|
|
7
|
+
* Watching explicit files avoids a broad recursive outDir glob watcher,
|
|
8
|
+
* which can add significant overhead on large docs sites.
|
|
9
|
+
*/
|
|
10
|
+
export declare function discoverOutputCssFiles(outDir: string): Promise<string[]>;
|
|
11
|
+
export interface RefreshCssOutputWatcherParams {
|
|
12
|
+
outDir: string;
|
|
13
|
+
logger: Logger;
|
|
14
|
+
wsServer: WebSocketServer | null;
|
|
15
|
+
disableWsReload: boolean;
|
|
16
|
+
cssWatcher: FSWatcher | null;
|
|
17
|
+
watchedCssFiles: Set<string>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reconciles the CSS watcher with the current output CSS file membership.
|
|
21
|
+
*
|
|
22
|
+
* If the file set changed, this closes the previous watcher and creates a new one
|
|
23
|
+
* bound to the latest concrete files.
|
|
24
|
+
*/
|
|
25
|
+
export declare function refreshCssOutputWatcher(params: RefreshCssOutputWatcherParams): Promise<FSWatcher | null>;
|
|
26
|
+
//# sourceMappingURL=dev-css-watcher.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-css-watcher.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/dev-css-watcher.utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAGnD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAa9E;AA0BD,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAgC3B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import fg from 'fast-glob';
|
|
3
|
+
import { pathExists } from './fs.utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Returns concrete CSS files currently present in outDir.
|
|
6
|
+
*
|
|
7
|
+
* Watching explicit files avoids a broad recursive outDir glob watcher,
|
|
8
|
+
* which can add significant overhead on large docs sites.
|
|
9
|
+
*/
|
|
10
|
+
export async function discoverOutputCssFiles(outDir) {
|
|
11
|
+
if (!(await pathExists(outDir))) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const cssFiles = await fg('**/*.css', {
|
|
15
|
+
cwd: outDir,
|
|
16
|
+
absolute: true,
|
|
17
|
+
onlyFiles: true,
|
|
18
|
+
dot: false,
|
|
19
|
+
});
|
|
20
|
+
return cssFiles;
|
|
21
|
+
}
|
|
22
|
+
function hasSameMembership(nextFiles, watchedCssFiles) {
|
|
23
|
+
if (nextFiles.length !== watchedCssFiles.size) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return nextFiles.every((file) => watchedCssFiles.has(file));
|
|
27
|
+
}
|
|
28
|
+
function broadcastReload(wsServer, disableWsReload, payload) {
|
|
29
|
+
if (!wsServer || disableWsReload) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
wsServer.clients.forEach((client) => {
|
|
33
|
+
const ws = client;
|
|
34
|
+
if (ws.readyState === 1) {
|
|
35
|
+
ws.send(payload);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Reconciles the CSS watcher with the current output CSS file membership.
|
|
41
|
+
*
|
|
42
|
+
* If the file set changed, this closes the previous watcher and creates a new one
|
|
43
|
+
* bound to the latest concrete files.
|
|
44
|
+
*/
|
|
45
|
+
export async function refreshCssOutputWatcher(params) {
|
|
46
|
+
const { outDir, logger, wsServer, disableWsReload, cssWatcher, watchedCssFiles } = params;
|
|
47
|
+
const cssFiles = await discoverOutputCssFiles(outDir);
|
|
48
|
+
if (hasSameMembership(cssFiles, watchedCssFiles)) {
|
|
49
|
+
return cssWatcher;
|
|
50
|
+
}
|
|
51
|
+
if (cssWatcher) {
|
|
52
|
+
await cssWatcher.close();
|
|
53
|
+
}
|
|
54
|
+
watchedCssFiles.clear();
|
|
55
|
+
cssFiles.forEach((file) => watchedCssFiles.add(file));
|
|
56
|
+
if (cssFiles.length === 0) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const nextCssWatcher = chokidar.watch(cssFiles, {
|
|
60
|
+
ignored: /(^|[/\\])\../,
|
|
61
|
+
persistent: true,
|
|
62
|
+
ignoreInitial: true,
|
|
63
|
+
});
|
|
64
|
+
nextCssWatcher.on('change', (path) => {
|
|
65
|
+
const relativePath = path.replace(process.cwd(), '').replace(/\\/g, '/').replace(/^\//, '');
|
|
66
|
+
logger.info?.(`▸ ${relativePath} updated`);
|
|
67
|
+
broadcastReload(wsServer, disableWsReload, JSON.stringify({ type: 'reload' }));
|
|
68
|
+
});
|
|
69
|
+
return nextCssWatcher;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/env.utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGnE"}
|
|
@@ -25,4 +25,7 @@ export { createFallbackLogger } from './logger.utils.js';
|
|
|
25
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
|
+
export { isEnabledEnvFlag } from './env.utils.js';
|
|
29
|
+
export { discoverOutputCssFiles, refreshCssOutputWatcher } from './dev-css-watcher.utils.js';
|
|
30
|
+
export type { RefreshCssOutputWatcherParams } from './dev-css-watcher.utils.js';
|
|
28
31
|
//# 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,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"}
|
|
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;AAG/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,YAAY,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/core/utils/index.js
CHANGED
|
@@ -38,3 +38,7 @@ export { createFallbackLogger } from './logger.utils.js';
|
|
|
38
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';
|
|
41
|
+
// Environment utilities
|
|
42
|
+
export { isEnabledEnvFlag } from './env.utils.js';
|
|
43
|
+
// Dev-server CSS watcher utilities
|
|
44
|
+
export { discoverOutputCssFiles, refreshCssOutputWatcher } from './dev-css-watcher.utils.js';
|
|
@@ -42,6 +42,11 @@ export interface WatchOptions {
|
|
|
42
42
|
logger: Logger;
|
|
43
43
|
/** Callback invoked when files are recompiled, receives results and compile time in ms */
|
|
44
44
|
onRebuild: (results: CompiledBundleInfo[], compileTimeMs: number) => void;
|
|
45
|
+
/**
|
|
46
|
+
* Whether to wait for the initial watch build to finish for all bundles.
|
|
47
|
+
* Useful during dev-server startup to ensure bundle files exist before page rendering.
|
|
48
|
+
*/
|
|
49
|
+
awaitInitialBuild?: boolean;
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
47
52
|
* Detects existing TypeScript bundles in the output directory.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/typescript.utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAInC,OAAO,EAA6B,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAQrD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,SAAS,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"typescript.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/typescript.utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAInC,OAAO,EAA6B,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAQrD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,SAAS,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAuCD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,GACtC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAyC/B;AAuED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkD9F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAyHjC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB5E;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAqC7D;AAiBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CA8B7E"}
|
|
@@ -217,12 +217,13 @@ export async function compileTypeScript(options) {
|
|
|
217
217
|
* ```
|
|
218
218
|
*/
|
|
219
219
|
export async function createTypeScriptWatcher(options) {
|
|
220
|
-
const { projectRoot, config, logger, onRebuild, outDir: globalOutDir } = options;
|
|
220
|
+
const { projectRoot, config, logger, onRebuild, outDir: globalOutDir, awaitInitialBuild = false, } = options;
|
|
221
221
|
const resolved = resolveConfig(config, 'development');
|
|
222
222
|
const outputDir = globalOutDir || DEFAULT_OUT_DIR;
|
|
223
223
|
const outDir = path.join(projectRoot, outputDir, resolved.outDir);
|
|
224
224
|
const contexts = [];
|
|
225
225
|
const latestResults = new Map();
|
|
226
|
+
const initialBuildPromises = [];
|
|
226
227
|
for (const bundleConfig of resolved.bundles) {
|
|
227
228
|
const entryPath = path.join(projectRoot, resolved.srcDir, bundleConfig.entryPoint);
|
|
228
229
|
// Validate entry point exists
|
|
@@ -230,10 +231,14 @@ export async function createTypeScriptWatcher(options) {
|
|
|
230
231
|
logger.warning(`TypeScript entry point not found: ${entryPath} (bundle: ${bundleConfig.bundleName})`);
|
|
231
232
|
continue;
|
|
232
233
|
}
|
|
233
|
-
// Track whether this is the initial build triggered by context.watch()
|
|
234
|
-
// We skip notifications for
|
|
235
|
-
// by compileTypeScript() during the initial build phase
|
|
234
|
+
// Track whether this is the initial build triggered by context.watch().
|
|
235
|
+
// We skip reload notifications for this build to avoid a reload storm during startup.
|
|
236
236
|
let isInitialBuild = true;
|
|
237
|
+
let resolveInitialBuild;
|
|
238
|
+
const initialBuildPromise = new Promise((resolve) => {
|
|
239
|
+
resolveInitialBuild = resolve;
|
|
240
|
+
});
|
|
241
|
+
initialBuildPromises.push(initialBuildPromise);
|
|
237
242
|
const context = await esbuild.context({
|
|
238
243
|
entryPoints: [entryPath],
|
|
239
244
|
bundle: true,
|
|
@@ -275,15 +280,21 @@ export async function createTypeScriptWatcher(options) {
|
|
|
275
280
|
path: bundlePath,
|
|
276
281
|
};
|
|
277
282
|
latestResults.set(bundleConfig.bundleName, bundleResult);
|
|
278
|
-
// Skip notifications for the initial build triggered by context.watch()
|
|
279
|
-
//
|
|
283
|
+
// Skip notifications for the initial build triggered by context.watch().
|
|
284
|
+
// This avoids triggering a reload while startup priming is still in progress.
|
|
280
285
|
if (isInitialBuild) {
|
|
281
286
|
isInitialBuild = false;
|
|
287
|
+
resolveInitialBuild();
|
|
282
288
|
return;
|
|
283
289
|
}
|
|
284
290
|
logger.info(`TypeScript '${bundleConfig.bundleName}' recompiled.`);
|
|
285
291
|
// Notify with all current results and compile time
|
|
286
292
|
onRebuild(Array.from(latestResults.values()), compileTime);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (isInitialBuild) {
|
|
296
|
+
isInitialBuild = false;
|
|
297
|
+
resolveInitialBuild();
|
|
287
298
|
}
|
|
288
299
|
});
|
|
289
300
|
},
|
|
@@ -294,6 +305,9 @@ export async function createTypeScriptWatcher(options) {
|
|
|
294
305
|
await context.watch();
|
|
295
306
|
contexts.push(context);
|
|
296
307
|
}
|
|
308
|
+
if (awaitInitialBuild && initialBuildPromises.length > 0) {
|
|
309
|
+
await Promise.all(initialBuildPromises);
|
|
310
|
+
}
|
|
297
311
|
if (contexts.length > 0) {
|
|
298
312
|
logger.info(`Watching TypeScript files in ${resolved.srcDir}/ (${contexts.length} bundle${contexts.length > 1 ? 's' : ''})`);
|
|
299
313
|
}
|