@zpress/cli 0.3.4 ā 0.4.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/README.md +9 -7
- package/dist/145.mjs +282 -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,164 @@ 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);
|
|
591
|
+
return uniq([
|
|
592
|
+
...dirs.map(toDirectory),
|
|
593
|
+
...CONFIG_GLOBS
|
|
594
|
+
]);
|
|
595
|
+
}
|
|
596
|
+
function flattenIncludePaths(sections) {
|
|
597
|
+
return sections.flatMap(flattenSection);
|
|
598
|
+
}
|
|
599
|
+
function flattenSection(section) {
|
|
600
|
+
const includes = normalizeInclude(section.include);
|
|
601
|
+
if (includes.length > 0 && section.items) return [
|
|
602
|
+
...includes,
|
|
603
|
+
...flattenIncludePaths(section.items)
|
|
604
|
+
];
|
|
605
|
+
if (includes.length > 0) return includes;
|
|
606
|
+
if (section.items) return flattenIncludePaths(section.items);
|
|
607
|
+
return [];
|
|
608
|
+
}
|
|
609
|
+
function toDirectory(from) {
|
|
610
|
+
if (hasGlobChars(from)) {
|
|
611
|
+
const normalized = from.replaceAll('\\', '/');
|
|
612
|
+
const segments = normalized.split('/');
|
|
613
|
+
const dirSegments = segments.filter((s)=>!hasGlobChars(s));
|
|
614
|
+
return dirSegments.join('/') || '.';
|
|
615
|
+
}
|
|
616
|
+
return from;
|
|
617
|
+
}
|
|
618
|
+
const RENAME_SEPARATOR = ' -> ';
|
|
619
|
+
function gitChangedFiles(params) {
|
|
620
|
+
const [err, output] = execSilent({
|
|
621
|
+
file: 'git',
|
|
622
|
+
args: [
|
|
623
|
+
'status',
|
|
624
|
+
'--short',
|
|
625
|
+
'--',
|
|
626
|
+
...params.dirs
|
|
627
|
+
],
|
|
628
|
+
cwd: params.repoRoot
|
|
629
|
+
});
|
|
630
|
+
if (err) return [
|
|
631
|
+
err,
|
|
632
|
+
null
|
|
633
|
+
];
|
|
634
|
+
if (!output) return [
|
|
635
|
+
null,
|
|
636
|
+
[]
|
|
637
|
+
];
|
|
638
|
+
const files = output.split('\n').filter((line)=>line.length > 0).map(parseStatusLine).filter((p)=>p.length > 0);
|
|
639
|
+
return [
|
|
640
|
+
null,
|
|
641
|
+
files
|
|
642
|
+
];
|
|
643
|
+
}
|
|
644
|
+
function parseStatusLine(line) {
|
|
645
|
+
const filePart = line.slice(3);
|
|
646
|
+
const renameIdx = filePart.indexOf(RENAME_SEPARATOR);
|
|
647
|
+
if (-1 !== renameIdx) return stripQuotes(filePart.slice(renameIdx + RENAME_SEPARATOR.length));
|
|
648
|
+
return stripQuotes(filePart);
|
|
649
|
+
}
|
|
650
|
+
function stripQuotes(value) {
|
|
651
|
+
const trimmed = value.trim();
|
|
652
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) return trimmed.slice(1, -1);
|
|
653
|
+
return trimmed;
|
|
654
|
+
}
|
|
655
|
+
function execSilent(params) {
|
|
656
|
+
try {
|
|
657
|
+
const output = execFileSync(params.file, [
|
|
658
|
+
...params.args
|
|
659
|
+
], {
|
|
660
|
+
cwd: params.cwd,
|
|
661
|
+
stdio: 'pipe',
|
|
662
|
+
encoding: 'utf8'
|
|
663
|
+
}).trimEnd();
|
|
664
|
+
return [
|
|
665
|
+
null,
|
|
666
|
+
output
|
|
667
|
+
];
|
|
668
|
+
} catch (error) {
|
|
669
|
+
if (error instanceof Error) return [
|
|
670
|
+
error,
|
|
671
|
+
null
|
|
672
|
+
];
|
|
673
|
+
return [
|
|
674
|
+
new Error(String(error)),
|
|
675
|
+
null
|
|
676
|
+
];
|
|
677
|
+
}
|
|
678
|
+
}
|
|
461
679
|
const registry = createRegistry();
|
|
462
680
|
const draftCommand = command({
|
|
463
681
|
description: 'Scaffold a new documentation file from a template',
|
|
@@ -557,9 +775,9 @@ function maybeHidden(hidden) {
|
|
|
557
775
|
};
|
|
558
776
|
return {};
|
|
559
777
|
}
|
|
560
|
-
function
|
|
561
|
-
if (
|
|
562
|
-
|
|
778
|
+
function maybeStandalone(standalone) {
|
|
779
|
+
if (standalone) return {
|
|
780
|
+
standalone
|
|
563
781
|
};
|
|
564
782
|
return {};
|
|
565
783
|
}
|
|
@@ -578,7 +796,7 @@ function buildDumpEntry(entry) {
|
|
|
578
796
|
...maybeLink(entry.link),
|
|
579
797
|
...maybeCollapsible(entry.collapsible),
|
|
580
798
|
...maybeHidden(entry.hidden),
|
|
581
|
-
...
|
|
799
|
+
...maybeStandalone(entry.standalone),
|
|
582
800
|
...maybeItems(entry.items)
|
|
583
801
|
};
|
|
584
802
|
}
|
|
@@ -629,7 +847,11 @@ function buildAssetConfig(config) {
|
|
|
629
847
|
const serveCommand = command({
|
|
630
848
|
description: 'Preview the built Rspress site',
|
|
631
849
|
options: z.object({
|
|
632
|
-
open: z.boolean().optional().default(true)
|
|
850
|
+
open: z.boolean().optional().default(true),
|
|
851
|
+
port: z.number().optional(),
|
|
852
|
+
theme: z.string().optional(),
|
|
853
|
+
colorMode: z.string().optional(),
|
|
854
|
+
vscode: z.boolean().optional().default(false)
|
|
633
855
|
}),
|
|
634
856
|
handler: async (ctx)=>{
|
|
635
857
|
ctx.logger.intro('zpress serve');
|
|
@@ -643,11 +865,15 @@ const serveCommand = command({
|
|
|
643
865
|
});
|
|
644
866
|
process.exit(1);
|
|
645
867
|
}
|
|
646
|
-
|
|
647
|
-
await serveSite({
|
|
868
|
+
const port = await serveSite({
|
|
648
869
|
config,
|
|
649
|
-
paths
|
|
870
|
+
paths,
|
|
871
|
+
port: ctx.args.port,
|
|
872
|
+
theme: ctx.args.theme,
|
|
873
|
+
colorMode: ctx.args.colorMode,
|
|
874
|
+
vscode: ctx.args.vscode
|
|
650
875
|
});
|
|
876
|
+
if (ctx.args.open) openBrowser(`http://localhost:${port}`);
|
|
651
877
|
}
|
|
652
878
|
});
|
|
653
879
|
const CONFIG_FILENAME = 'zpress.config.ts';
|
|
@@ -666,6 +892,7 @@ const setupCommand = command({
|
|
|
666
892
|
const title = deriveTitle(cwd);
|
|
667
893
|
node_fs.writeFileSync(configPath, buildConfigTemplate(title), 'utf8');
|
|
668
894
|
ctx.logger.success(`Created ${CONFIG_FILENAME} (title: "${title}")`);
|
|
895
|
+
await ensureGitignore(paths, ctx.logger);
|
|
669
896
|
await promises.mkdir(paths.publicDir, {
|
|
670
897
|
recursive: true
|
|
671
898
|
});
|
|
@@ -685,7 +912,7 @@ const setupCommand = command({
|
|
|
685
912
|
}
|
|
686
913
|
});
|
|
687
914
|
function extractGitRepoName(cwd) {
|
|
688
|
-
const url =
|
|
915
|
+
const url = setup_execSilent('git', [
|
|
689
916
|
'remote',
|
|
690
917
|
'get-url',
|
|
691
918
|
'origin'
|
|
@@ -695,7 +922,7 @@ function extractGitRepoName(cwd) {
|
|
|
695
922
|
if (!match) return null;
|
|
696
923
|
return match[1];
|
|
697
924
|
}
|
|
698
|
-
function
|
|
925
|
+
function setup_execSilent(file, args, cwd) {
|
|
699
926
|
try {
|
|
700
927
|
return execFileSync(file, [
|
|
701
928
|
...args
|
|
@@ -721,14 +948,43 @@ export default defineConfig({
|
|
|
721
948
|
title: '${escaped}',
|
|
722
949
|
sections: [
|
|
723
950
|
{
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
951
|
+
title: 'Getting Started',
|
|
952
|
+
path: '/getting-started',
|
|
953
|
+
include: 'docs/*.md',
|
|
727
954
|
},
|
|
728
955
|
],
|
|
729
956
|
})
|
|
730
957
|
`;
|
|
731
958
|
}
|
|
959
|
+
const ZPRESS_GITIGNORE_ENTRY = '.zpress/';
|
|
960
|
+
const NESTED_GITIGNORE_CONTENT = `# managed by zpress ā ignore everything by default
|
|
961
|
+
*
|
|
962
|
+
|
|
963
|
+
# to track custom assets (e.g. banners, logos), uncomment the following lines:
|
|
964
|
+
# !public/
|
|
965
|
+
# !public/**
|
|
966
|
+
`;
|
|
967
|
+
async function ensureGitignore(paths, logger) {
|
|
968
|
+
const rootGitignore = node_path.join(paths.repoRoot, '.gitignore');
|
|
969
|
+
if (node_fs.existsSync(rootGitignore)) {
|
|
970
|
+
const content = node_fs.readFileSync(rootGitignore, 'utf8');
|
|
971
|
+
const lines = content.split('\n');
|
|
972
|
+
const alreadyIgnored = lines.some((line)=>line.trim() === ZPRESS_GITIGNORE_ENTRY || '.zpress' === line.trim());
|
|
973
|
+
if (alreadyIgnored) return;
|
|
974
|
+
const suffix = (()=>{
|
|
975
|
+
if (content.endsWith('\n')) return '';
|
|
976
|
+
return '\n';
|
|
977
|
+
})();
|
|
978
|
+
node_fs.writeFileSync(rootGitignore, `${content}${suffix}\n# zpress\n${ZPRESS_GITIGNORE_ENTRY}\n`, 'utf8');
|
|
979
|
+
logger.success('Added .zpress/ to .gitignore');
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
await promises.mkdir(paths.outputRoot, {
|
|
983
|
+
recursive: true
|
|
984
|
+
});
|
|
985
|
+
await promises.writeFile(node_path.join(paths.outputRoot, '.gitignore'), NESTED_GITIGNORE_CONTENT, 'utf8');
|
|
986
|
+
logger.success('Created .zpress/.gitignore');
|
|
987
|
+
}
|
|
732
988
|
const syncCommand = command({
|
|
733
989
|
description: 'Sync documentation sources into .zpress/',
|
|
734
990
|
options: z.object({
|
|
@@ -756,11 +1012,12 @@ const syncCommand = command({
|
|
|
756
1012
|
});
|
|
757
1013
|
await cli({
|
|
758
1014
|
name: 'zpress',
|
|
759
|
-
version: "0.
|
|
1015
|
+
version: "0.4.0",
|
|
760
1016
|
description: 'CLI for building and serving documentation',
|
|
761
1017
|
commands: {
|
|
762
1018
|
sync: syncCommand,
|
|
763
1019
|
dev: devCommand,
|
|
1020
|
+
diff: diffCommand,
|
|
764
1021
|
build: buildCommand,
|
|
765
1022
|
check: checkCommand,
|
|
766
1023
|
draft: draftCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zpress/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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.0"
|
|
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
|
}
|