@zpress/cli 0.3.0 → 0.3.2

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 CHANGED
@@ -436,9 +436,9 @@ const devCommand = command({
436
436
  const [configErr, config] = await loadConfig(paths.repoRoot);
437
437
  if (configErr) {
438
438
  ctx.logger.error(configErr.message);
439
- if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
439
+ if (configErr.errors && configErr.errors.length > 0) configErr.errors.forEach((err)=>{
440
440
  const path = err.path.join('.');
441
- return ctx.logger.error(` ${path}: ${err.message}`);
441
+ ctx.logger.error(` ${path}: ${err.message}`);
442
442
  });
443
443
  process.exit(1);
444
444
  }
@@ -453,7 +453,7 @@ const devCommand = command({
453
453
  const { createWatcher } = await import("./watcher.mjs");
454
454
  const watcher = createWatcher(config, paths, onConfigReload);
455
455
  function cleanup() {
456
- if (watcher) watcher.close();
456
+ watcher.close();
457
457
  }
458
458
  process.on('SIGINT', cleanup);
459
459
  process.on('SIGTERM', cleanup);
@@ -710,7 +710,7 @@ const syncCommand = command({
710
710
  });
711
711
  await cli({
712
712
  name: 'zpress',
713
- version: "0.3.0",
713
+ version: "0.3.2",
714
714
  description: 'CLI for building and serving documentation',
715
715
  commands: {
716
716
  sync: syncCommand,
package/dist/watcher.mjs CHANGED
@@ -1,10 +1,8 @@
1
- import { existsSync } from "node:fs";
1
+ import { watch } from "node:fs";
2
2
  import node_path from "node:path";
3
3
  import { cliLogger } from "@kidd-cli/core/logger";
4
- import { hasGlobChars, loadConfig, sync } from "@zpress/core";
5
- import { watch } from "chokidar";
4
+ import { loadConfig, sync } from "@zpress/core";
6
5
  import { debounce } from "es-toolkit";
7
- import { match } from "ts-pattern";
8
6
  const CONFIG_EXTENSIONS = [
9
7
  '.ts',
10
8
  '.mts',
@@ -18,86 +16,29 @@ const MARKDOWN_EXTENSIONS = [
18
16
  '.md',
19
17
  '.mdx'
20
18
  ];
21
- const MAX_DISPLAY_PATHS = 3;
19
+ const IGNORED_DIRS = new Set([
20
+ 'node_modules',
21
+ '.git',
22
+ '.zpress',
23
+ 'bundle',
24
+ 'dist',
25
+ '.turbo'
26
+ ]);
22
27
  function isMarkdownFile(filePath) {
23
28
  return MARKDOWN_EXTENSIONS.some((ext)=>filePath.endsWith(ext));
24
29
  }
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;
30
+ function isIgnored(filePath) {
31
+ return filePath.split(node_path.sep).some((segment)=>IGNORED_DIRS.has(segment));
33
32
  }
34
33
  function createWatcher(initialConfig, paths, onConfigReload) {
35
34
  const { repoRoot } = paths;
36
- const configFiles = CONFIG_EXTENSIONS.map((ext)=>node_path.resolve(repoRoot, `zpress.config${ext}`));
35
+ const configFileNames = new Set(CONFIG_EXTENSIONS.map((ext)=>`zpress.config${ext}`));
37
36
  let config = initialConfig;
38
- const planningDir = node_path.resolve(repoRoot, '.planning');
39
- const contentPaths = extractWatchPaths(config.sections, repoRoot).map((p)=>nearestExistingAncestor(p, repoRoot));
40
- const initialWatchPaths = [
41
- ...contentPaths,
42
- nearestExistingAncestor(planningDir, repoRoot),
43
- ...configFiles
44
- ];
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}`);
37
+ cliLogger.info(`Watching ${repoRoot}`);
52
38
  let syncing = false;
53
39
  let pendingReloadConfig = null;
54
40
  let consecutiveFailures = 0;
55
41
  const MAX_CONSECUTIVE_FAILURES = 5;
56
- let currentWatchPaths = new Set(uniqueInitialPaths);
57
- const watcher = watch(uniqueInitialPaths, {
58
- ignoreInitial: true,
59
- ignored: [
60
- '**/node_modules/**',
61
- '**/.git/**',
62
- '**/.zpress/**',
63
- '**/bundle/**'
64
- ],
65
- awaitWriteFinish: {
66
- stabilityThreshold: 100,
67
- pollInterval: 50
68
- },
69
- depth: 99
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
- }
101
42
  async function triggerSync(reloadConfig) {
102
43
  if (syncing) {
103
44
  pendingReloadConfig = true === pendingReloadConfig || reloadConfig;
@@ -110,15 +51,14 @@ function createWatcher(initialConfig, paths, onConfigReload) {
110
51
  const [configErr, newConfig] = await loadConfig(paths.repoRoot);
111
52
  if (configErr) {
112
53
  cliLogger.error(`Config reload failed: ${configErr.message}`);
113
- if (configErr.errors && configErr.errors.length > 0) configErr.errors.map((err)=>{
54
+ if (configErr.errors && configErr.errors.length > 0) configErr.errors.forEach((err)=>{
114
55
  const pathStr = err.path.join('.');
115
- return cliLogger.error(` ${pathStr}: ${err.message}`);
56
+ cliLogger.error(` ${pathStr}: ${err.message}`);
116
57
  });
117
58
  return;
118
59
  }
119
60
  config = newConfig;
120
61
  cliLogger.info('Config reloaded');
121
- updateWatchPaths(newConfig);
122
62
  didReloadConfig = true;
123
63
  }
124
64
  await sync(config, {
@@ -148,60 +88,30 @@ function createWatcher(initialConfig, paths, onConfigReload) {
148
88
  }
149
89
  const debouncedSync = debounce(()=>triggerSync(false), 150);
150
90
  const debouncedConfigSync = debounce(()=>triggerSync(true), 150);
151
- const configFileSet = new Set(configFiles);
152
- function isConfigFile(filePath) {
153
- return configFileSet.has(node_path.resolve(filePath));
91
+ function isConfigFile(filename, filePath) {
92
+ if (!configFileNames.has(filename)) return false;
93
+ const dir = node_path.dirname(filePath);
94
+ return '.' === dir;
154
95
  }
155
- watcher.on('change', (filePath)=>{
156
- if (isConfigFile(filePath)) {
157
- cliLogger.info(`Config changed: ${node_path.basename(filePath)}`);
96
+ const watcher = watch(repoRoot, {
97
+ recursive: true
98
+ }, (_event, filename)=>{
99
+ if (!filename) return;
100
+ if (isIgnored(filename)) return;
101
+ const basename = node_path.basename(filename);
102
+ if (isConfigFile(basename, filename)) {
103
+ cliLogger.info(`Config changed: ${basename}`);
158
104
  debouncedConfigSync();
159
105
  return;
160
106
  }
161
- if (!isMarkdownFile(filePath)) return;
162
- cliLogger.step(`Changed: ${node_path.relative(repoRoot, filePath)}`);
163
- debouncedSync();
164
- });
165
- watcher.on('add', (filePath)=>{
166
- if (!isMarkdownFile(filePath)) return;
167
- cliLogger.step(`Added: ${node_path.relative(repoRoot, filePath)}`);
107
+ if (!isMarkdownFile(filename)) return;
108
+ cliLogger.step(`Changed: ${filename}`);
168
109
  debouncedSync();
169
110
  });
170
- watcher.on('unlink', (filePath)=>{
171
- if (!isMarkdownFile(filePath)) return;
172
- cliLogger.step(`Removed: ${node_path.relative(repoRoot, filePath)}`);
173
- debouncedSync();
174
- });
175
- return watcher;
176
- }
177
- function extractWatchPaths(entries, repoRoot) {
178
- const dirs = new Set();
179
- const files = new Set();
180
- function walk(items) {
181
- items.map((entry)=>{
182
- if (entry.from) if (hasGlobChars(entry.from)) {
183
- const [beforeGlob] = entry.from.split('*');
184
- const dir = match(beforeGlob.endsWith('/')).with(true, ()=>beforeGlob.slice(0, -1)).otherwise(()=>node_path.dirname(beforeGlob));
185
- dirs.add(node_path.resolve(repoRoot, dir));
186
- } else files.add(node_path.resolve(repoRoot, entry.from));
187
- if (entry.items) walk(entry.items);
188
- return null;
189
- });
190
- }
191
- walk(entries);
192
- const sortedDirs = [
193
- ...dirs
194
- ].toSorted();
195
- const dedupedDirs = sortedDirs.filter((dir, index)=>{
196
- const previousDirs = sortedDirs.slice(0, index);
197
- return !previousDirs.some((parent)=>dir.startsWith(`${parent}${node_path.sep}`));
198
- });
199
- const extraFiles = [
200
- ...files
201
- ].filter((file)=>!dedupedDirs.some((dir)=>file.startsWith(dir + node_path.sep)));
202
- return [
203
- ...dedupedDirs,
204
- ...extraFiles
205
- ];
111
+ return {
112
+ close () {
113
+ watcher.close();
114
+ }
115
+ };
206
116
  }
207
117
  export { createWatcher };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zpress/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "CLI for building and serving zpress documentation sites",
5
5
  "keywords": [
6
6
  "cli",
@@ -36,12 +36,11 @@
36
36
  "dependencies": {
37
37
  "@kidd-cli/core": "^0.4.0",
38
38
  "@rspress/core": "^2.0.5",
39
- "chokidar": "^5.0.0",
40
39
  "es-toolkit": "^1.45.1",
41
40
  "ts-pattern": "^5.9.0",
42
41
  "zod": "^4.3.6",
43
- "@zpress/core": "0.6.0",
44
- "@zpress/ui": "0.5.0"
42
+ "@zpress/core": "0.6.2",
43
+ "@zpress/ui": "0.6.0"
45
44
  },
46
45
  "devDependencies": {
47
46
  "@rslib/core": "^0.20.0",
@@ -53,6 +52,7 @@
53
52
  "scripts": {
54
53
  "build": "rslib build",
55
54
  "dev": "node ./dist/index.mjs dev --cwd ../..",
55
+ "test": "vitest run",
56
56
  "typecheck": "tsc --noEmit"
57
57
  }
58
58
  }