@zpress/cli 0.2.1 ā 0.3.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/index.mjs +73 -18
- package/dist/watcher.mjs +68 -14
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -12,18 +12,49 @@ import promises from "node:fs/promises";
|
|
|
12
12
|
import node_fs from "node:fs";
|
|
13
13
|
const DEFAULT_PORT = 6174;
|
|
14
14
|
async function startDevServer(options) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
const { paths } = options;
|
|
16
|
+
let serverInstance = null;
|
|
17
|
+
async function startServer(config) {
|
|
18
|
+
const rspressConfig = createRspressConfig({
|
|
19
|
+
config,
|
|
20
|
+
paths
|
|
21
|
+
});
|
|
22
|
+
try {
|
|
23
|
+
serverInstance = await dev({
|
|
24
|
+
appDirectory: paths.repoRoot,
|
|
25
|
+
docDirectory: paths.contentDir,
|
|
26
|
+
config: rspressConfig,
|
|
27
|
+
configFilePath: '',
|
|
28
|
+
extraBuilderConfig: {
|
|
29
|
+
server: {
|
|
30
|
+
port: DEFAULT_PORT
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
} catch (error) {
|
|
35
|
+
const errorMessage = (()=>{
|
|
36
|
+
if (error instanceof Error) return error.message;
|
|
37
|
+
return String(error);
|
|
38
|
+
})();
|
|
39
|
+
process.stderr.write(`Dev server error: ${errorMessage}\n`);
|
|
40
|
+
process.exit(1);
|
|
25
41
|
}
|
|
26
|
-
}
|
|
42
|
+
}
|
|
43
|
+
await startServer(options.config);
|
|
44
|
+
return async (newConfig)=>{
|
|
45
|
+
process.stdout.write('\nš Config changed ā restarting dev server...\n');
|
|
46
|
+
if (serverInstance) try {
|
|
47
|
+
await serverInstance.close();
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const errorMessage = (()=>{
|
|
50
|
+
if (error instanceof Error) return error.message;
|
|
51
|
+
return String(error);
|
|
52
|
+
})();
|
|
53
|
+
process.stderr.write(`Error closing server: ${errorMessage}\n`);
|
|
54
|
+
}
|
|
55
|
+
await startServer(newConfig);
|
|
56
|
+
process.stdout.write('ā
Dev server restarted\n\n');
|
|
57
|
+
};
|
|
27
58
|
}
|
|
28
59
|
async function buildSite(options) {
|
|
29
60
|
const rspressConfig = createRspressConfig(options);
|
|
@@ -297,6 +328,10 @@ const buildCommand = command({
|
|
|
297
328
|
const [configErr, config] = await loadConfig(paths.repoRoot);
|
|
298
329
|
if (configErr) {
|
|
299
330
|
ctx.logger.error(configErr.message);
|
|
331
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
|
|
332
|
+
const path = err.path.join('.');
|
|
333
|
+
return ctx.logger.error(` ${path}: ${err.message}`);
|
|
334
|
+
});
|
|
300
335
|
process.exit(1);
|
|
301
336
|
}
|
|
302
337
|
if (check) {
|
|
@@ -401,23 +436,27 @@ const devCommand = command({
|
|
|
401
436
|
const [configErr, config] = await loadConfig(paths.repoRoot);
|
|
402
437
|
if (configErr) {
|
|
403
438
|
ctx.logger.error(configErr.message);
|
|
439
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
|
|
440
|
+
const path = err.path.join('.');
|
|
441
|
+
return ctx.logger.error(` ${path}: ${err.message}`);
|
|
442
|
+
});
|
|
404
443
|
process.exit(1);
|
|
405
444
|
}
|
|
406
445
|
await sync(config, {
|
|
407
446
|
paths,
|
|
408
447
|
quiet
|
|
409
448
|
});
|
|
449
|
+
const onConfigReload = await startDevServer({
|
|
450
|
+
config,
|
|
451
|
+
paths
|
|
452
|
+
});
|
|
410
453
|
const { createWatcher } = await import("./watcher.mjs");
|
|
411
|
-
const watcher = createWatcher(config, paths);
|
|
454
|
+
const watcher = createWatcher(config, paths, onConfigReload);
|
|
412
455
|
function cleanup() {
|
|
413
456
|
if (watcher) watcher.close();
|
|
414
457
|
}
|
|
415
458
|
process.on('SIGINT', cleanup);
|
|
416
459
|
process.on('SIGTERM', cleanup);
|
|
417
|
-
await startDevServer({
|
|
418
|
-
config,
|
|
419
|
-
paths
|
|
420
|
-
});
|
|
421
460
|
}
|
|
422
461
|
});
|
|
423
462
|
function maybeLink(link) {
|
|
@@ -455,7 +494,7 @@ function toTree(entries) {
|
|
|
455
494
|
}
|
|
456
495
|
function buildDumpEntry(entry) {
|
|
457
496
|
return {
|
|
458
|
-
text: entry.
|
|
497
|
+
text: entry.title,
|
|
459
498
|
...maybeLink(entry.link),
|
|
460
499
|
...maybeCollapsible(entry.collapsible),
|
|
461
500
|
...maybeHidden(entry.hidden),
|
|
@@ -470,6 +509,10 @@ const dumpCommand = command({
|
|
|
470
509
|
const [configErr, config] = await loadConfig(paths.repoRoot);
|
|
471
510
|
if (configErr) {
|
|
472
511
|
ctx.logger.error(configErr.message);
|
|
512
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
|
|
513
|
+
const path = err.path.join('.');
|
|
514
|
+
return ctx.logger.error(` ${path}: ${err.message}`);
|
|
515
|
+
});
|
|
473
516
|
process.exit(1);
|
|
474
517
|
}
|
|
475
518
|
const previousManifest = await loadManifest(paths.contentDir);
|
|
@@ -508,6 +551,10 @@ const generateCommand = command({
|
|
|
508
551
|
const [configErr, config] = await loadConfig(paths.repoRoot);
|
|
509
552
|
if (configErr) {
|
|
510
553
|
ctx.logger.error(configErr.message);
|
|
554
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
|
|
555
|
+
const path = err.path.join('.');
|
|
556
|
+
return ctx.logger.error(` ${path}: ${err.message}`);
|
|
557
|
+
});
|
|
511
558
|
process.exit(1);
|
|
512
559
|
}
|
|
513
560
|
const assetConfig = buildAssetConfig(config);
|
|
@@ -544,6 +591,10 @@ const serveCommand = command({
|
|
|
544
591
|
const [configErr, config] = await loadConfig(paths.repoRoot);
|
|
545
592
|
if (configErr) {
|
|
546
593
|
ctx.logger.error(configErr.message);
|
|
594
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
|
|
595
|
+
const path = err.path.join('.');
|
|
596
|
+
return ctx.logger.error(` ${path}: ${err.message}`);
|
|
597
|
+
});
|
|
547
598
|
process.exit(1);
|
|
548
599
|
}
|
|
549
600
|
if (ctx.args.open) setTimeout(()=>openBrowser(`http://localhost:${DEFAULT_PORT}`), 2000);
|
|
@@ -644,6 +695,10 @@ const syncCommand = command({
|
|
|
644
695
|
const [configErr, config] = await loadConfig(paths.repoRoot);
|
|
645
696
|
if (configErr) {
|
|
646
697
|
ctx.logger.error(configErr.message);
|
|
698
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
|
|
699
|
+
const path = err.path.join('.');
|
|
700
|
+
return ctx.logger.error(` ${path}: ${err.message}`);
|
|
701
|
+
});
|
|
647
702
|
process.exit(1);
|
|
648
703
|
}
|
|
649
704
|
await sync(config, {
|
|
@@ -655,7 +710,7 @@ const syncCommand = command({
|
|
|
655
710
|
});
|
|
656
711
|
await cli({
|
|
657
712
|
name: 'zpress',
|
|
658
|
-
version: "0.
|
|
713
|
+
version: "0.3.0",
|
|
659
714
|
description: 'CLI for building and serving documentation',
|
|
660
715
|
commands: {
|
|
661
716
|
sync: syncCommand,
|
package/dist/watcher.mjs
CHANGED
|
@@ -18,31 +18,43 @@ const MARKDOWN_EXTENSIONS = [
|
|
|
18
18
|
'.md',
|
|
19
19
|
'.mdx'
|
|
20
20
|
];
|
|
21
|
+
const MAX_DISPLAY_PATHS = 3;
|
|
21
22
|
function isMarkdownFile(filePath) {
|
|
22
23
|
return MARKDOWN_EXTENSIONS.some((ext)=>filePath.endsWith(ext));
|
|
23
24
|
}
|
|
24
|
-
function
|
|
25
|
+
function nearestExistingAncestor(targetPath, fallbackRoot) {
|
|
26
|
+
let current = targetPath;
|
|
27
|
+
if (existsSync(current)) return current;
|
|
28
|
+
while(current !== node_path.dirname(current)){
|
|
29
|
+
current = node_path.dirname(current);
|
|
30
|
+
if (existsSync(current)) return current;
|
|
31
|
+
}
|
|
32
|
+
return fallbackRoot;
|
|
33
|
+
}
|
|
34
|
+
function createWatcher(initialConfig, paths, onConfigReload) {
|
|
25
35
|
const { repoRoot } = paths;
|
|
26
36
|
const configFiles = CONFIG_EXTENSIONS.map((ext)=>node_path.resolve(repoRoot, `zpress.config${ext}`));
|
|
27
37
|
let config = initialConfig;
|
|
28
38
|
const planningDir = node_path.resolve(repoRoot, '.planning');
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
...
|
|
32
|
-
|
|
33
|
-
planningDir
|
|
34
|
-
];
|
|
35
|
-
return [];
|
|
36
|
-
})(),
|
|
39
|
+
const contentPaths = extractWatchPaths(config.sections, repoRoot).map((p)=>nearestExistingAncestor(p, repoRoot));
|
|
40
|
+
const initialWatchPaths = [
|
|
41
|
+
...contentPaths,
|
|
42
|
+
nearestExistingAncestor(planningDir, repoRoot),
|
|
37
43
|
...configFiles
|
|
38
44
|
];
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
const uniqueInitialPaths = [
|
|
46
|
+
...new Set(initialWatchPaths)
|
|
47
|
+
];
|
|
48
|
+
if (0 === uniqueInitialPaths.length) return void cliLogger.warn('No source paths to watch');
|
|
49
|
+
const relativePaths = uniqueInitialPaths.map((p)=>node_path.relative(repoRoot, p));
|
|
50
|
+
const pathsMessage = match(relativePaths.length <= MAX_DISPLAY_PATHS).with(true, ()=>relativePaths.join(', ')).otherwise(()=>`${relativePaths.slice(0, MAX_DISPLAY_PATHS).join(', ')} and ${relativePaths.length - MAX_DISPLAY_PATHS} more`);
|
|
51
|
+
cliLogger.info(`Watching ${uniqueInitialPaths.length} paths: ${pathsMessage}`);
|
|
41
52
|
let syncing = false;
|
|
42
53
|
let pendingReloadConfig = null;
|
|
43
54
|
let consecutiveFailures = 0;
|
|
44
55
|
const MAX_CONSECUTIVE_FAILURES = 5;
|
|
45
|
-
|
|
56
|
+
let currentWatchPaths = new Set(uniqueInitialPaths);
|
|
57
|
+
const watcher = watch(uniqueInitialPaths, {
|
|
46
58
|
ignoreInitial: true,
|
|
47
59
|
ignored: [
|
|
48
60
|
'**/node_modules/**',
|
|
@@ -53,25 +65,67 @@ function createWatcher(initialConfig, paths) {
|
|
|
53
65
|
awaitWriteFinish: {
|
|
54
66
|
stabilityThreshold: 100,
|
|
55
67
|
pollInterval: 50
|
|
56
|
-
}
|
|
68
|
+
},
|
|
69
|
+
depth: 99
|
|
57
70
|
});
|
|
71
|
+
function updateWatchPaths(newConfig) {
|
|
72
|
+
const newContentPaths = extractWatchPaths(newConfig.sections, repoRoot);
|
|
73
|
+
const normalizedContentPaths = newContentPaths.map((p)=>nearestExistingAncestor(p, repoRoot));
|
|
74
|
+
const newWatchPaths = [
|
|
75
|
+
...normalizedContentPaths,
|
|
76
|
+
nearestExistingAncestor(planningDir, repoRoot),
|
|
77
|
+
...configFiles
|
|
78
|
+
];
|
|
79
|
+
const newSet = new Set(newWatchPaths);
|
|
80
|
+
const toAdd = [
|
|
81
|
+
...newSet
|
|
82
|
+
].filter((p)=>!currentWatchPaths.has(p));
|
|
83
|
+
if (toAdd.length > 0) {
|
|
84
|
+
watcher.add(toAdd);
|
|
85
|
+
const relativeAdded = toAdd.map((p)=>node_path.relative(repoRoot, p));
|
|
86
|
+
const addedMessage = match(relativeAdded.length <= MAX_DISPLAY_PATHS).with(true, ()=>relativeAdded.join(', ')).otherwise(()=>`${relativeAdded.slice(0, MAX_DISPLAY_PATHS).join(', ')} and ${relativeAdded.length - MAX_DISPLAY_PATHS} more`);
|
|
87
|
+
cliLogger.info(`Added ${toAdd.length} watch paths: ${addedMessage}`);
|
|
88
|
+
}
|
|
89
|
+
const configFileSet = new Set(configFiles);
|
|
90
|
+
const toRemove = [
|
|
91
|
+
...currentWatchPaths
|
|
92
|
+
].filter((p)=>!newSet.has(p) && !configFileSet.has(p));
|
|
93
|
+
if (toRemove.length > 0) {
|
|
94
|
+
watcher.unwatch(toRemove);
|
|
95
|
+
const relativeRemoved = toRemove.map((p)=>node_path.relative(repoRoot, p));
|
|
96
|
+
const removedMessage = match(relativeRemoved.length <= MAX_DISPLAY_PATHS).with(true, ()=>relativeRemoved.join(', ')).otherwise(()=>`${relativeRemoved.slice(0, MAX_DISPLAY_PATHS).join(', ')} and ${relativeRemoved.length - MAX_DISPLAY_PATHS} more`);
|
|
97
|
+
cliLogger.info(`Removed ${toRemove.length} watch paths: ${removedMessage}`);
|
|
98
|
+
}
|
|
99
|
+
currentWatchPaths = newSet;
|
|
100
|
+
}
|
|
58
101
|
async function triggerSync(reloadConfig) {
|
|
59
102
|
if (syncing) {
|
|
60
103
|
pendingReloadConfig = true === pendingReloadConfig || reloadConfig;
|
|
61
104
|
return;
|
|
62
105
|
}
|
|
63
106
|
syncing = true;
|
|
107
|
+
let didReloadConfig = false;
|
|
64
108
|
try {
|
|
65
109
|
if (reloadConfig) {
|
|
66
110
|
const [configErr, newConfig] = await loadConfig(paths.repoRoot);
|
|
67
|
-
if (configErr)
|
|
111
|
+
if (configErr) {
|
|
112
|
+
cliLogger.error(`Config reload failed: ${configErr.message}`);
|
|
113
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
|
|
114
|
+
const pathStr = err.path.join('.');
|
|
115
|
+
return cliLogger.error(` ${pathStr}: ${err.message}`);
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
68
119
|
config = newConfig;
|
|
69
120
|
cliLogger.info('Config reloaded');
|
|
121
|
+
updateWatchPaths(newConfig);
|
|
122
|
+
didReloadConfig = true;
|
|
70
123
|
}
|
|
71
124
|
await sync(config, {
|
|
72
125
|
paths
|
|
73
126
|
});
|
|
74
127
|
consecutiveFailures = 0;
|
|
128
|
+
if (didReloadConfig && onConfigReload) await onConfigReload(config);
|
|
75
129
|
} catch (error) {
|
|
76
130
|
consecutiveFailures += 1;
|
|
77
131
|
const errorMessage = (()=>{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zpress/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for building and serving zpress documentation sites",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"es-toolkit": "^1.45.1",
|
|
41
41
|
"ts-pattern": "^5.9.0",
|
|
42
42
|
"zod": "^4.3.6",
|
|
43
|
-
"@zpress/core": "0.
|
|
44
|
-
"@zpress/ui": "0.
|
|
43
|
+
"@zpress/core": "0.6.0",
|
|
44
|
+
"@zpress/ui": "0.5.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@rslib/core": "^0.20.0",
|