@zpress/cli 0.3.4 ā 0.4.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/README.md +9 -7
- package/dist/145.mjs +289 -25
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="https://raw.githubusercontent.com/joggrdocs/zpress/main/assets/banner.svg" alt="zpress" width="90%" />
|
|
3
|
-
<p><strong>CLI for building and serving zpress documentation sites.</strong></p>
|
|
1
|
+
# @zpress/cli
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
<a href="https://www.npmjs.com/package/@zpress/cli"><img src="https://img.shields.io/npm/v/@zpress/cli" alt="npm version" /></a>
|
|
7
|
-
<a href="https://github.com/joggrdocs/zpress/blob/main/LICENSE"><img src="https://img.shields.io/github/license/joggrdocs/zpress" alt="License" /></a>
|
|
3
|
+
CLI for building and serving zpress documentation sites.
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
<span class="zp-badge">
|
|
6
|
+
|
|
7
|
+
[](https://github.com/joggrdocs/zpress/actions/workflows/ci.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/@zpress/cli)
|
|
9
|
+
[](https://github.com/joggrdocs/zpress/blob/main/LICENSE)
|
|
10
|
+
|
|
11
|
+
</span>
|
|
10
12
|
|
|
11
13
|
## Install
|
|
12
14
|
|
package/dist/145.mjs
CHANGED
|
@@ -1,28 +1,42 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { cli, command } from "@kidd-cli/core";
|
|
2
|
-
import { configError, createPaths, generateAssets, loadConfig, loadManifest, resolveEntries, sync } from "@zpress/core";
|
|
3
|
+
import { configError, createPaths, generateAssets, hasGlobChars, loadConfig, loadManifest, normalizeInclude, resolveEntries, sync } from "@zpress/core";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import node_path from "node:path";
|
|
5
6
|
import { execFileSync, spawn } from "node:child_process";
|
|
7
|
+
import { once } from "node:events";
|
|
8
|
+
import { Server } from "node:http";
|
|
6
9
|
import { platform } from "node:os";
|
|
7
10
|
import { build, dev, serve } from "@rspress/core";
|
|
8
11
|
import { createRspressConfig } from "@zpress/ui";
|
|
12
|
+
import get_port, { portNumbers } from "get-port";
|
|
9
13
|
import { P, match as external_ts_pattern_match } from "ts-pattern";
|
|
10
14
|
import promises from "node:fs/promises";
|
|
11
|
-
import { compact } from "es-toolkit";
|
|
15
|
+
import { compact, uniq } from "es-toolkit";
|
|
12
16
|
import { createRegistry, render, toSlug } from "@zpress/templates";
|
|
13
17
|
import node_fs from "node:fs";
|
|
18
|
+
globalThis.require = globalThis.require ?? createRequire(import.meta.url);
|
|
14
19
|
function toError(error) {
|
|
15
20
|
if (error instanceof Error) return error;
|
|
16
21
|
return new Error(String(error));
|
|
17
22
|
}
|
|
18
|
-
const
|
|
23
|
+
const DEV_PORT = 6174;
|
|
24
|
+
const DEV_PORT_RANGE = 5;
|
|
25
|
+
const SERVE_PORT = 8080;
|
|
19
26
|
async function startDevServer(options) {
|
|
20
27
|
const { paths } = options;
|
|
28
|
+
const preferred = options.port ?? DEV_PORT;
|
|
29
|
+
const port = await get_port({
|
|
30
|
+
port: portNumbers(preferred, preferred + DEV_PORT_RANGE)
|
|
31
|
+
});
|
|
21
32
|
let serverInstance = null;
|
|
22
33
|
async function startServer(config) {
|
|
23
34
|
const rspressConfig = createRspressConfig({
|
|
24
35
|
config,
|
|
25
|
-
paths
|
|
36
|
+
paths,
|
|
37
|
+
vscode: options.vscode,
|
|
38
|
+
themeOverride: options.theme,
|
|
39
|
+
colorModeOverride: options.colorMode
|
|
26
40
|
});
|
|
27
41
|
try {
|
|
28
42
|
serverInstance = await dev({
|
|
@@ -32,7 +46,8 @@ async function startDevServer(options) {
|
|
|
32
46
|
configFilePath: '',
|
|
33
47
|
extraBuilderConfig: {
|
|
34
48
|
server: {
|
|
35
|
-
port
|
|
49
|
+
port,
|
|
50
|
+
strictPort: true
|
|
36
51
|
}
|
|
37
52
|
}
|
|
38
53
|
});
|
|
@@ -47,11 +62,20 @@ async function startDevServer(options) {
|
|
|
47
62
|
return async (newConfig)=>{
|
|
48
63
|
process.stdout.write('\nš Config changed ā restarting dev server...\n');
|
|
49
64
|
if (serverInstance) {
|
|
65
|
+
const httpServer = getHttpServer(serverInstance);
|
|
66
|
+
const closeEvent = createCloseEvent(httpServer);
|
|
50
67
|
try {
|
|
51
68
|
await serverInstance.close();
|
|
52
69
|
} catch (error) {
|
|
53
70
|
process.stderr.write(`Error closing server: ${toError(error).message}\n`);
|
|
54
71
|
}
|
|
72
|
+
if (closeEvent) {
|
|
73
|
+
const PORT_RELEASE_TIMEOUT = 5000;
|
|
74
|
+
await Promise.race([
|
|
75
|
+
closeEvent,
|
|
76
|
+
new Promise((resolve)=>setTimeout(resolve, PORT_RELEASE_TIMEOUT))
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
55
79
|
serverInstance = null;
|
|
56
80
|
}
|
|
57
81
|
const restarted = await startServer(newConfig);
|
|
@@ -60,7 +84,10 @@ async function startDevServer(options) {
|
|
|
60
84
|
};
|
|
61
85
|
}
|
|
62
86
|
async function buildSite(options) {
|
|
63
|
-
const rspressConfig = createRspressConfig(
|
|
87
|
+
const rspressConfig = createRspressConfig({
|
|
88
|
+
config: options.config,
|
|
89
|
+
paths: options.paths
|
|
90
|
+
});
|
|
64
91
|
await build({
|
|
65
92
|
docDirectory: options.paths.contentDir,
|
|
66
93
|
config: rspressConfig,
|
|
@@ -68,7 +95,10 @@ async function buildSite(options) {
|
|
|
68
95
|
});
|
|
69
96
|
}
|
|
70
97
|
async function buildSiteForCheck(options) {
|
|
71
|
-
const rspressConfig = createRspressConfig(
|
|
98
|
+
const rspressConfig = createRspressConfig({
|
|
99
|
+
config: options.config,
|
|
100
|
+
paths: options.paths
|
|
101
|
+
});
|
|
72
102
|
await build({
|
|
73
103
|
docDirectory: options.paths.contentDir,
|
|
74
104
|
config: rspressConfig,
|
|
@@ -76,12 +106,23 @@ async function buildSiteForCheck(options) {
|
|
|
76
106
|
});
|
|
77
107
|
}
|
|
78
108
|
async function serveSite(options) {
|
|
79
|
-
const rspressConfig = createRspressConfig(
|
|
109
|
+
const rspressConfig = createRspressConfig({
|
|
110
|
+
config: options.config,
|
|
111
|
+
paths: options.paths,
|
|
112
|
+
vscode: options.vscode,
|
|
113
|
+
themeOverride: options.theme,
|
|
114
|
+
colorModeOverride: options.colorMode
|
|
115
|
+
});
|
|
116
|
+
const preferredPort = options.port ?? SERVE_PORT;
|
|
117
|
+
const port = await get_port({
|
|
118
|
+
port: portNumbers(preferredPort, preferredPort + DEV_PORT_RANGE)
|
|
119
|
+
});
|
|
80
120
|
await serve({
|
|
81
121
|
config: rspressConfig,
|
|
82
122
|
configFilePath: '',
|
|
83
|
-
port
|
|
123
|
+
port
|
|
84
124
|
});
|
|
125
|
+
return port;
|
|
85
126
|
}
|
|
86
127
|
function openBrowser(url) {
|
|
87
128
|
const os = platform();
|
|
@@ -108,6 +149,17 @@ function openBrowser(url) {
|
|
|
108
149
|
detached: true
|
|
109
150
|
}).unref();
|
|
110
151
|
}
|
|
152
|
+
function createCloseEvent(httpServer) {
|
|
153
|
+
if (null === httpServer) return null;
|
|
154
|
+
if (!httpServer.listening) return null;
|
|
155
|
+
return once(httpServer, 'close');
|
|
156
|
+
}
|
|
157
|
+
function getHttpServer(instance) {
|
|
158
|
+
const record = instance;
|
|
159
|
+
const value = record['httpServer'];
|
|
160
|
+
if (value instanceof Server) return value;
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
111
163
|
const ANSI_PATTERN = /\u001B\[[0-9;]*m/g;
|
|
112
164
|
const RED = '\u001B[31m';
|
|
113
165
|
const DIM = '\u001B[2m';
|
|
@@ -422,7 +474,11 @@ const devCommand = command({
|
|
|
422
474
|
description: 'Run sync + watcher and start Rspress dev server',
|
|
423
475
|
options: z.object({
|
|
424
476
|
quiet: z.boolean().optional().default(false),
|
|
425
|
-
clean: z.boolean().optional().default(false)
|
|
477
|
+
clean: z.boolean().optional().default(false),
|
|
478
|
+
port: z.number().optional(),
|
|
479
|
+
theme: z.string().optional(),
|
|
480
|
+
colorMode: z.string().optional(),
|
|
481
|
+
vscode: z.boolean().optional().default(false)
|
|
426
482
|
}),
|
|
427
483
|
handler: async (ctx)=>{
|
|
428
484
|
const { quiet } = ctx.args;
|
|
@@ -447,7 +503,11 @@ const devCommand = command({
|
|
|
447
503
|
});
|
|
448
504
|
const onConfigReload = await startDevServer({
|
|
449
505
|
config,
|
|
450
|
-
paths
|
|
506
|
+
paths,
|
|
507
|
+
port: ctx.args.port,
|
|
508
|
+
theme: ctx.args.theme,
|
|
509
|
+
colorMode: ctx.args.colorMode,
|
|
510
|
+
vscode: ctx.args.vscode
|
|
451
511
|
});
|
|
452
512
|
const { createWatcher } = await import("./watcher.mjs");
|
|
453
513
|
const watcher = createWatcher(config, paths, onConfigReload);
|
|
@@ -458,6 +518,171 @@ const devCommand = command({
|
|
|
458
518
|
process.on('SIGTERM', cleanup);
|
|
459
519
|
}
|
|
460
520
|
});
|
|
521
|
+
const CONFIG_GLOBS = [
|
|
522
|
+
'zpress.config.ts',
|
|
523
|
+
'zpress.config.mts',
|
|
524
|
+
'zpress.config.cts',
|
|
525
|
+
'zpress.config.js',
|
|
526
|
+
'zpress.config.mjs',
|
|
527
|
+
'zpress.config.cjs',
|
|
528
|
+
'zpress.config.json'
|
|
529
|
+
];
|
|
530
|
+
const diffCommand = command({
|
|
531
|
+
description: 'Show changed files in configured source directories',
|
|
532
|
+
options: z.object({
|
|
533
|
+
pretty: z.boolean().optional().default(false)
|
|
534
|
+
}),
|
|
535
|
+
handler: async (ctx)=>{
|
|
536
|
+
const { pretty } = ctx.args;
|
|
537
|
+
const paths = createPaths(process.cwd());
|
|
538
|
+
const [configErr, config] = await loadConfig(paths.repoRoot);
|
|
539
|
+
if (configErr) {
|
|
540
|
+
if (pretty) {
|
|
541
|
+
ctx.logger.intro('zpress diff');
|
|
542
|
+
ctx.logger.error(configErr.message);
|
|
543
|
+
if (configErr.errors && configErr.errors.length > 0) configErr.errors.forEach((err)=>{
|
|
544
|
+
const p = err.path.join('.');
|
|
545
|
+
ctx.logger.error(` ${p}: ${err.message}`);
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
const dirs = collectWatchPaths(config);
|
|
551
|
+
if (0 === dirs.length) {
|
|
552
|
+
if (pretty) {
|
|
553
|
+
ctx.logger.intro('zpress diff');
|
|
554
|
+
ctx.logger.warn('No source directories found in config');
|
|
555
|
+
ctx.logger.outro('Done');
|
|
556
|
+
}
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const [gitErr, changed] = gitChangedFiles({
|
|
560
|
+
repoRoot: paths.repoRoot,
|
|
561
|
+
dirs
|
|
562
|
+
});
|
|
563
|
+
if (gitErr) {
|
|
564
|
+
if (pretty) {
|
|
565
|
+
ctx.logger.intro('zpress diff');
|
|
566
|
+
ctx.logger.error(`Git failed: ${gitErr.message}`);
|
|
567
|
+
ctx.logger.outro('Done');
|
|
568
|
+
}
|
|
569
|
+
process.exit(1);
|
|
570
|
+
}
|
|
571
|
+
if (0 === changed.length) {
|
|
572
|
+
if (pretty) {
|
|
573
|
+
ctx.logger.intro('zpress diff');
|
|
574
|
+
ctx.logger.success('No changes detected');
|
|
575
|
+
ctx.logger.outro('Done');
|
|
576
|
+
}
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (pretty) {
|
|
580
|
+
ctx.logger.intro('zpress diff');
|
|
581
|
+
ctx.logger.step(`Watching ${dirs.length} path(s)`);
|
|
582
|
+
ctx.logger.note(changed.join('\n'), `${changed.length} changed file(s)`);
|
|
583
|
+
ctx.logger.outro('Done');
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
process.stdout.write(`${changed.join(' ')}\n`);
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
function collectWatchPaths(config) {
|
|
590
|
+
const dirs = flattenIncludePaths(config.sections).map(toDirectory);
|
|
591
|
+
const roots = dirs.map(toTopLevelRoot).filter((r)=>r.length > 0);
|
|
592
|
+
return uniq([
|
|
593
|
+
...dirs,
|
|
594
|
+
...roots,
|
|
595
|
+
...CONFIG_GLOBS
|
|
596
|
+
]);
|
|
597
|
+
}
|
|
598
|
+
function flattenIncludePaths(sections) {
|
|
599
|
+
return sections.flatMap(flattenSection);
|
|
600
|
+
}
|
|
601
|
+
function flattenSection(section) {
|
|
602
|
+
const includes = normalizeInclude(section.include);
|
|
603
|
+
if (includes.length > 0 && section.items) return [
|
|
604
|
+
...includes,
|
|
605
|
+
...flattenIncludePaths(section.items)
|
|
606
|
+
];
|
|
607
|
+
if (includes.length > 0) return includes;
|
|
608
|
+
if (section.items) return flattenIncludePaths(section.items);
|
|
609
|
+
return [];
|
|
610
|
+
}
|
|
611
|
+
function toDirectory(from) {
|
|
612
|
+
if (hasGlobChars(from)) {
|
|
613
|
+
const normalized = from.replaceAll('\\', '/');
|
|
614
|
+
const segments = normalized.split('/');
|
|
615
|
+
const dirSegments = segments.filter((s)=>!hasGlobChars(s));
|
|
616
|
+
return dirSegments.join('/') || '.';
|
|
617
|
+
}
|
|
618
|
+
return from;
|
|
619
|
+
}
|
|
620
|
+
function toTopLevelRoot(dir) {
|
|
621
|
+
const idx = dir.indexOf('/');
|
|
622
|
+
if (-1 === idx) return '';
|
|
623
|
+
return dir.slice(0, idx + 1);
|
|
624
|
+
}
|
|
625
|
+
const RENAME_SEPARATOR = ' -> ';
|
|
626
|
+
function gitChangedFiles(params) {
|
|
627
|
+
const [err, output] = execSilent({
|
|
628
|
+
file: 'git',
|
|
629
|
+
args: [
|
|
630
|
+
'status',
|
|
631
|
+
'--short',
|
|
632
|
+
'--',
|
|
633
|
+
...params.dirs
|
|
634
|
+
],
|
|
635
|
+
cwd: params.repoRoot
|
|
636
|
+
});
|
|
637
|
+
if (err) return [
|
|
638
|
+
err,
|
|
639
|
+
null
|
|
640
|
+
];
|
|
641
|
+
if (!output) return [
|
|
642
|
+
null,
|
|
643
|
+
[]
|
|
644
|
+
];
|
|
645
|
+
const files = output.split('\n').filter((line)=>line.length > 0).map(parseStatusLine).filter((p)=>p.length > 0);
|
|
646
|
+
return [
|
|
647
|
+
null,
|
|
648
|
+
files
|
|
649
|
+
];
|
|
650
|
+
}
|
|
651
|
+
function parseStatusLine(line) {
|
|
652
|
+
const filePart = line.slice(3);
|
|
653
|
+
const renameIdx = filePart.indexOf(RENAME_SEPARATOR);
|
|
654
|
+
if (-1 !== renameIdx) return stripQuotes(filePart.slice(renameIdx + RENAME_SEPARATOR.length));
|
|
655
|
+
return stripQuotes(filePart);
|
|
656
|
+
}
|
|
657
|
+
function stripQuotes(value) {
|
|
658
|
+
const trimmed = value.trim();
|
|
659
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) return trimmed.slice(1, -1);
|
|
660
|
+
return trimmed;
|
|
661
|
+
}
|
|
662
|
+
function execSilent(params) {
|
|
663
|
+
try {
|
|
664
|
+
const output = execFileSync(params.file, [
|
|
665
|
+
...params.args
|
|
666
|
+
], {
|
|
667
|
+
cwd: params.cwd,
|
|
668
|
+
stdio: 'pipe',
|
|
669
|
+
encoding: 'utf8'
|
|
670
|
+
}).trimEnd();
|
|
671
|
+
return [
|
|
672
|
+
null,
|
|
673
|
+
output
|
|
674
|
+
];
|
|
675
|
+
} catch (error) {
|
|
676
|
+
if (error instanceof Error) return [
|
|
677
|
+
error,
|
|
678
|
+
null
|
|
679
|
+
];
|
|
680
|
+
return [
|
|
681
|
+
new Error(String(error)),
|
|
682
|
+
null
|
|
683
|
+
];
|
|
684
|
+
}
|
|
685
|
+
}
|
|
461
686
|
const registry = createRegistry();
|
|
462
687
|
const draftCommand = command({
|
|
463
688
|
description: 'Scaffold a new documentation file from a template',
|
|
@@ -557,9 +782,9 @@ function maybeHidden(hidden) {
|
|
|
557
782
|
};
|
|
558
783
|
return {};
|
|
559
784
|
}
|
|
560
|
-
function
|
|
561
|
-
if (
|
|
562
|
-
|
|
785
|
+
function maybeStandalone(standalone) {
|
|
786
|
+
if (standalone) return {
|
|
787
|
+
standalone
|
|
563
788
|
};
|
|
564
789
|
return {};
|
|
565
790
|
}
|
|
@@ -578,7 +803,7 @@ function buildDumpEntry(entry) {
|
|
|
578
803
|
...maybeLink(entry.link),
|
|
579
804
|
...maybeCollapsible(entry.collapsible),
|
|
580
805
|
...maybeHidden(entry.hidden),
|
|
581
|
-
...
|
|
806
|
+
...maybeStandalone(entry.standalone),
|
|
582
807
|
...maybeItems(entry.items)
|
|
583
808
|
};
|
|
584
809
|
}
|
|
@@ -629,7 +854,11 @@ function buildAssetConfig(config) {
|
|
|
629
854
|
const serveCommand = command({
|
|
630
855
|
description: 'Preview the built Rspress site',
|
|
631
856
|
options: z.object({
|
|
632
|
-
open: z.boolean().optional().default(true)
|
|
857
|
+
open: z.boolean().optional().default(true),
|
|
858
|
+
port: z.number().optional(),
|
|
859
|
+
theme: z.string().optional(),
|
|
860
|
+
colorMode: z.string().optional(),
|
|
861
|
+
vscode: z.boolean().optional().default(false)
|
|
633
862
|
}),
|
|
634
863
|
handler: async (ctx)=>{
|
|
635
864
|
ctx.logger.intro('zpress serve');
|
|
@@ -643,11 +872,15 @@ const serveCommand = command({
|
|
|
643
872
|
});
|
|
644
873
|
process.exit(1);
|
|
645
874
|
}
|
|
646
|
-
|
|
647
|
-
await serveSite({
|
|
875
|
+
const port = await serveSite({
|
|
648
876
|
config,
|
|
649
|
-
paths
|
|
877
|
+
paths,
|
|
878
|
+
port: ctx.args.port,
|
|
879
|
+
theme: ctx.args.theme,
|
|
880
|
+
colorMode: ctx.args.colorMode,
|
|
881
|
+
vscode: ctx.args.vscode
|
|
650
882
|
});
|
|
883
|
+
if (ctx.args.open) openBrowser(`http://localhost:${port}`);
|
|
651
884
|
}
|
|
652
885
|
});
|
|
653
886
|
const CONFIG_FILENAME = 'zpress.config.ts';
|
|
@@ -666,6 +899,7 @@ const setupCommand = command({
|
|
|
666
899
|
const title = deriveTitle(cwd);
|
|
667
900
|
node_fs.writeFileSync(configPath, buildConfigTemplate(title), 'utf8');
|
|
668
901
|
ctx.logger.success(`Created ${CONFIG_FILENAME} (title: "${title}")`);
|
|
902
|
+
await ensureGitignore(paths, ctx.logger);
|
|
669
903
|
await promises.mkdir(paths.publicDir, {
|
|
670
904
|
recursive: true
|
|
671
905
|
});
|
|
@@ -685,7 +919,7 @@ const setupCommand = command({
|
|
|
685
919
|
}
|
|
686
920
|
});
|
|
687
921
|
function extractGitRepoName(cwd) {
|
|
688
|
-
const url =
|
|
922
|
+
const url = setup_execSilent('git', [
|
|
689
923
|
'remote',
|
|
690
924
|
'get-url',
|
|
691
925
|
'origin'
|
|
@@ -695,7 +929,7 @@ function extractGitRepoName(cwd) {
|
|
|
695
929
|
if (!match) return null;
|
|
696
930
|
return match[1];
|
|
697
931
|
}
|
|
698
|
-
function
|
|
932
|
+
function setup_execSilent(file, args, cwd) {
|
|
699
933
|
try {
|
|
700
934
|
return execFileSync(file, [
|
|
701
935
|
...args
|
|
@@ -721,14 +955,43 @@ export default defineConfig({
|
|
|
721
955
|
title: '${escaped}',
|
|
722
956
|
sections: [
|
|
723
957
|
{
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
958
|
+
title: 'Getting Started',
|
|
959
|
+
path: '/getting-started',
|
|
960
|
+
include: 'docs/*.md',
|
|
727
961
|
},
|
|
728
962
|
],
|
|
729
963
|
})
|
|
730
964
|
`;
|
|
731
965
|
}
|
|
966
|
+
const ZPRESS_GITIGNORE_ENTRY = '.zpress/';
|
|
967
|
+
const NESTED_GITIGNORE_CONTENT = `# managed by zpress ā ignore everything by default
|
|
968
|
+
*
|
|
969
|
+
|
|
970
|
+
# to track custom assets (e.g. banners, logos), uncomment the following lines:
|
|
971
|
+
# !public/
|
|
972
|
+
# !public/**
|
|
973
|
+
`;
|
|
974
|
+
async function ensureGitignore(paths, logger) {
|
|
975
|
+
const rootGitignore = node_path.join(paths.repoRoot, '.gitignore');
|
|
976
|
+
if (node_fs.existsSync(rootGitignore)) {
|
|
977
|
+
const content = node_fs.readFileSync(rootGitignore, 'utf8');
|
|
978
|
+
const lines = content.split('\n');
|
|
979
|
+
const alreadyIgnored = lines.some((line)=>line.trim() === ZPRESS_GITIGNORE_ENTRY || '.zpress' === line.trim());
|
|
980
|
+
if (alreadyIgnored) return;
|
|
981
|
+
const suffix = (()=>{
|
|
982
|
+
if (content.endsWith('\n')) return '';
|
|
983
|
+
return '\n';
|
|
984
|
+
})();
|
|
985
|
+
node_fs.writeFileSync(rootGitignore, `${content}${suffix}\n# zpress\n${ZPRESS_GITIGNORE_ENTRY}\n`, 'utf8');
|
|
986
|
+
logger.success('Added .zpress/ to .gitignore');
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
await promises.mkdir(paths.outputRoot, {
|
|
990
|
+
recursive: true
|
|
991
|
+
});
|
|
992
|
+
await promises.writeFile(node_path.join(paths.outputRoot, '.gitignore'), NESTED_GITIGNORE_CONTENT, 'utf8');
|
|
993
|
+
logger.success('Created .zpress/.gitignore');
|
|
994
|
+
}
|
|
732
995
|
const syncCommand = command({
|
|
733
996
|
description: 'Sync documentation sources into .zpress/',
|
|
734
997
|
options: z.object({
|
|
@@ -756,11 +1019,12 @@ const syncCommand = command({
|
|
|
756
1019
|
});
|
|
757
1020
|
await cli({
|
|
758
1021
|
name: 'zpress',
|
|
759
|
-
version: "0.
|
|
1022
|
+
version: "0.4.1",
|
|
760
1023
|
description: 'CLI for building and serving documentation',
|
|
761
1024
|
commands: {
|
|
762
1025
|
sync: syncCommand,
|
|
763
1026
|
dev: devCommand,
|
|
1027
|
+
diff: diffCommand,
|
|
764
1028
|
build: buildCommand,
|
|
765
1029
|
check: checkCommand,
|
|
766
1030
|
draft: draftCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zpress/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "CLI for building and serving zpress documentation sites",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -34,14 +34,15 @@
|
|
|
34
34
|
"provenance": true
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@kidd-cli/core": "^0.
|
|
38
|
-
"@rspress/core": "^2.0.
|
|
37
|
+
"@kidd-cli/core": "^0.10.0",
|
|
38
|
+
"@rspress/core": "^2.0.6",
|
|
39
39
|
"es-toolkit": "^1.45.1",
|
|
40
|
+
"get-port": "^7.1.0",
|
|
40
41
|
"ts-pattern": "^5.9.0",
|
|
41
42
|
"zod": "^4.3.6",
|
|
42
|
-
"@zpress/core": "0.7.
|
|
43
|
+
"@zpress/core": "0.7.1",
|
|
43
44
|
"@zpress/templates": "0.1.1",
|
|
44
|
-
"@zpress/ui": "0.
|
|
45
|
+
"@zpress/ui": "0.8.1"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@rslib/core": "^0.20.0",
|
|
@@ -55,6 +56,6 @@
|
|
|
55
56
|
"build": "rslib build",
|
|
56
57
|
"dev": "node ./dist/index.mjs dev --cwd ../..",
|
|
57
58
|
"test": "vitest run",
|
|
58
|
-
"typecheck": "
|
|
59
|
+
"typecheck": "tsgo --noEmit"
|
|
59
60
|
}
|
|
60
61
|
}
|