Skip to content

fix(packages/cli,deps): fix EMFILE error and upgrade to zod 4.x and type-fest 5.x#27

Merged
zrosenbauer merged 3 commits intomainfrom
fix/watcher-emfile-upgrade-deps
Mar 13, 2026
Merged

fix(packages/cli,deps): fix EMFILE error and upgrade to zod 4.x and type-fest 5.x#27
zrosenbauer merged 3 commits intomainfrom
fix/watcher-emfile-upgrade-deps

Conversation

@zrosenbauer
Copy link
Member

@zrosenbauer zrosenbauer commented Mar 13, 2026

Summary

  • Fixes EMFILE: too many open files error when watching many directories on macOS
  • Improves CLI output to show only first 3 watched paths + count for better readability
  • Upgrades zod to 4.3.6 and type-fest to 5.4.4 with all necessary compatibility fixes

Changes

Watcher Fixes (packages/cli)

  • Added depth: 99 limit to chokidar to reduce file descriptor usage
  • Changed path logging to show max 3 paths + count (e.g., "Watching 34 paths: path1, path2, path3 and 31 more")
  • Uses ts-pattern match instead of ternaries for functional style compliance

Dependency Upgrades

  • zod: 3.24.14.3.6 (added to workspace catalog)
  • type-fest: 4.30.05.4.4 (added to workspace catalog)
  • turbo: 2.8.162.8.17

Zod 4.x Compatibility (packages/config)

  • Updated API calls: zodError.errorszodError.issues
  • Fixed tuple syntax: z.tuple([...])z.array(...).min(2).max(2)
  • Fixed record signature: z.record(key, value) now requires both parameters
  • Added type guards for path filtering (symbols no longer allowed)
  • Fixed zod-to-json-schema compatibility with type assertion

Code Style (packages/cli)

  • Replaced forEach loops with .map() to comply with functional/no-loop-statements rule

Test Plan

  • pnpm check passes (typecheck + lint + format)
  • All packages build successfully
  • Watcher output displays correctly with condensed path list

Summary by CodeRabbit

  • New Features

    • TitleConfig, a new base Entry type, and Discovery options for richer workspace/content configuration.
    • Injects critical CSS for built-in themes to improve initial rendering.
  • Breaking Changes

    • workspace item field renamed: path → prefix.
    • workspace category field renamed: name → title.
    • Default titleFrom changed from "filename" → "auto".
  • Improvements

    • VS Code UI tweaks (hidden mobile nav in VS Code mode).
    • Condensed log path display.
    • Documentation refreshed and reformatted.

…ype-fest 5.x

- Fix EMFILE (too many open files) error on macOS by adding depth limit to chokidar
- Improve watcher output to show only first 3 paths + count instead of listing all paths
- Upgrade zod to 4.3.6 (from 3.24.1) with compatibility fixes for new API
- Upgrade type-fest to 5.4.4 (from 4.30.0)
- Upgrade turbo to 2.8.17
- Add zod and type-fest to pnpm workspace catalog for consistency
- Fix Zod 4.x API changes: errors -> issues, tuple syntax, record signature
- Replace forEach with map to comply with functional programming rules

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 13, 2026

⚠️ No Changeset found

Latest commit: c388dc2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 6 packages
Name Type
@zpress/theme Minor
@zpress/config Minor
@zpress/core Major
@zpress/ui Major
@zpress/cli Major
@zpress/kit Major

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

Refactors and expands the configuration schema/API (new Entry base type, TitleConfig, Discovery; renames like pathprefix, nametitle), adds UI critical-css support and head injection, tightens schemas with zod v3/.strict(), updates watcher logging and chokidar depth, switches some deps to catalog:, and applies wide formatting/docs edits.

Changes

Cohort / File(s) Summary
Config schema & validation
packages/config/src/schema.ts, packages/config/schemas/schema.json, packages/config/src/errors.ts, packages/config/src/validator.ts, packages/config/scripts/generate-schema.ts
Large schema refactor: new Entry/TitleConfig/Discovery/card fields, stricter .strict() schemas, zod v3 adjustments, added titleFrom enums, sanitized zod issue path handling, and small generate-schema typing change.
Workspace API & docs
.changeset/refactor-workspace-config-api.md, docs/guides/workspaces.md, .changeset/config-theme-packages.md
Docs/changeset updates documenting API renames (pathprefix, nametitle), TitleConfig/Discovery guidance, and formatted workspaces docs.
UI critical CSS & head injection
packages/ui/src/critical-css.ts, packages/ui/src/config.ts
Added critical-css module mapping built-in theme names to minified CSS and injects a <style data-zpress-critical> into returned head elements when applicable.
Watcher & CLI error logging
packages/cli/src/lib/watcher.ts, packages/cli/src/commands/*.ts
Introduced MAX_DISPLAY_PATHS truncation for logged paths, chokidar depth safety option, unified truncated messages, and replaced several forEach usages with map in error logging callbacks (behavior unchanged).
Schema generation & config package metadata
packages/config/README.md, packages/config/package.json, packages/config/schemas/schema.json
README examples reformatted; package deps/devDeps updated (some → catalog:); JSON schema inline enum/formatting changes and two new properties added.
Dependency catalog & workspace
pnpm-workspace.yaml, package.json, packages/theme/package.json, packages/cli/package.json
Added catalog entries and switched several dependency specs to catalog:; bumped select devDeps.
Docs & icons formatting
docs/references/icons/*.mdx, packages/theme/README.md, packages/config/README.md
Widespread formatting and reflow of icon tables and TechIconTable entries; multi-line arrays replace compact literals; README code-block cleanup.
Core sync, resolve & small refactors
packages/core/src/sync/..., packages/core/src/sync/resolve/..., packages/core/src/define-config.ts
Import deduplication and formatting/refactorings around titleConfig extraction and small whitespace changes; logic preserved.
VS Code workspace & extension
.vscode/*.code-workspace, .vscode/settings.json, extensions/vscode/src/extension.ts
JSON trailing-comma/newline formatting and minor multi-line refactor of setContext call; no behavioral change.
Type & signature formatting
packages/theme/src/types.ts, packages/cli/src/lib/rspress.ts, packages/core/src/sync/index.ts
Type unions and function signatures reformatted across multiple lines; no semantic changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibble lines and tidy trails,

Entries bloom where old paths hailed,
Prefixes hop, titles find light,
Critical styles snug and tight,
A rabbit cheers — code looks just right! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing an EMFILE error by adding chokidar depth limiting, upgrading zod and type-fest dependencies, and related CLI/config updates to support the new versions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/watcher-emfile-upgrade-deps
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Fixes Flash of Unstyled Content (FOUC) by injecting minified critical
CSS directly into HTML <head> during build. Critical CSS includes
essential color variables and html/body background styling for
immediate first paint, while full CSS loads asynchronously.

Changes:
- Add critical-css.ts with minified theme-specific critical CSS
- Update config.ts to inject critical CSS via Rspress head config
- Support all built-in themes (base, midnight, arcade, arcade-fx)

Impact:
- Eliminates ~100-300ms white flash on cold page loads
- Improves first paint experience on production deployments
- ~1KB inline CSS per theme

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
packages/config/src/errors.ts (1)

49-60: Add JSDoc for configErrorFromZod.

This exported helper is part of the package surface and is touched in this PR, but it still has no in-source doc block.

As per coding guidelines "Include JSDoc comments on exported functions".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/config/src/errors.ts` around lines 49 - 60, Add a JSDoc block for
the exported function configErrorFromZod describing its purpose (convert a
ZodError into a ConfigError), the parameter zodError (type ZodError) and what it
contains, and the return value (ConfigError with _tag, type, message, and errors
array), and include any relevant notes about the shape of errors.path
(string|number) and that this is part of the public package surface; place the
JSDoc immediately above the configErrorFromZod function declaration to satisfy
the exported-function documentation guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/config/package.json`:
- Line 56: The project is passing Zod v4 schemas to zod-to-json-schema v3 which
expects Zod v3; fix by either updating the dependency "zod-to-json-schema" in
package.json to a release that supports Zod v4, or change the import in
packages/config/src/schema.ts to import { z } from 'zod/v3' so the schemas
passed to zodToJsonSchema match the expected Zod version; locate references to z
and zodToJsonSchema in schema.ts (and ensure package.json dependency bump is
published compatible) and update accordingly.

In `@packages/config/README.md`:
- Line 365: The README's ConfigErrorType union is incomplete; update the
documented union to include every exported case from
packages/config/src/errors.ts so consumers can exhaustively handle errors —
replace the current type ConfigErrorType = 'not_found' | 'parse_error' |
'validation_failed' | 'empty_sections' with the full set (add 'missing_field',
'invalid_entry', 'invalid_section', 'invalid_field', 'invalid_icon',
'invalid_theme', 'duplicate_prefix', 'unknown') and ensure the README's API
reference for ConfigErrorType matches the actual exports in errors.ts (reference
symbol: ConfigErrorType and the exported cases in errors.ts).

In `@packages/config/src/schema.ts`:
- Around line 26-33: The current frontmatter.head schema uses
z.array(...).min(2).max(2) which allows any ordering of the two types; change it
to enforce fixed positions by replacing the inner array with a tuple (use
z.tuple) so the first element is z.string() and the second is
z.record(z.string(), z.string()); keep the outer z.array(...) and .optional()
semantics but make each item a z.tuple([z.string(), z.record(z.string(),
z.string())]) so consumers relying on positional semantics get validated data.
- Around line 91-151: The schemas (entrySchema, workspaceItemSchema,
workspaceGroupSchema, featureSchema) are too strict and no longer match the
documented/migrated config shapes, causing validateConfig (used by loadConfig)
to reject real user configs; update these Zod schemas to accept the
legacy/advertised fields and optional titles (e.g., allow title OR text,
description, titleFrom, titleTransform as optional alternatives), restore
top-level discovery fields on workspaceItemSchema, and relax or remove .strict()
where necessary (or add .passthrough()) so unknown-but-documented fields are
allowed; ensure validateConfig still enforces required new constraints but does
not reject configs following the README migration examples.

---

Nitpick comments:
In `@packages/config/src/errors.ts`:
- Around line 49-60: Add a JSDoc block for the exported function
configErrorFromZod describing its purpose (convert a ZodError into a
ConfigError), the parameter zodError (type ZodError) and what it contains, and
the return value (ConfigError with _tag, type, message, and errors array), and
include any relevant notes about the shape of errors.path (string|number) and
that this is part of the public package surface; place the JSDoc immediately
above the configErrorFromZod function declaration to satisfy the
exported-function documentation guideline.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 623bbd68-166b-48b6-b41d-9014d14ee5a8

📥 Commits

Reviewing files that changed from the base of the PR and between b6c1d2a and ea57c61.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (46)
  • .changeset/config-theme-packages.md
  • .changeset/refactor-workspace-config-api.md
  • .changeset/vscode-ui-improvements.md
  • .vscode/kitchen-sink.code-workspace
  • .vscode/settings.json
  • .vscode/simple.code-workspace
  • docs/guides/workspaces.md
  • docs/references/icons/colors.mdx
  • docs/references/icons/overview.mdx
  • docs/references/icons/technology/databases.mdx
  • docs/references/icons/technology/frameworks.mdx
  • docs/references/icons/technology/infrastructure.mdx
  • docs/references/icons/technology/integrations.mdx
  • docs/references/icons/technology/languages.mdx
  • docs/references/icons/technology/overview.mdx
  • docs/references/icons/technology/tooling.mdx
  • extensions/vscode/src/extension.ts
  • package.json
  • packages/cli/package.json
  • packages/cli/src/commands/build.ts
  • packages/cli/src/commands/dev.ts
  • packages/cli/src/commands/dump.ts
  • packages/cli/src/commands/generate.ts
  • packages/cli/src/commands/serve.ts
  • packages/cli/src/commands/sync.ts
  • packages/cli/src/lib/rspress.ts
  • packages/cli/src/lib/watcher.ts
  • packages/config/README.md
  • packages/config/package.json
  • packages/config/schemas/schema.json
  • packages/config/scripts/generate-schema.ts
  • packages/config/src/errors.ts
  • packages/config/src/loader.ts
  • packages/config/src/schema.ts
  • packages/config/src/validator.ts
  • packages/core/src/define-config.ts
  • packages/core/src/sync/home.ts
  • packages/core/src/sync/index.ts
  • packages/core/src/sync/resolve/index.ts
  • packages/core/src/sync/resolve/recursive.ts
  • packages/core/src/sync/workspace.ts
  • packages/theme/README.md
  • packages/theme/package.json
  • packages/theme/src/types.ts
  • packages/ui/src/config.ts
  • pnpm-workspace.yaml
💤 Files with no reviewable changes (1)
  • packages/core/src/sync/workspace.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/ui/src/config.ts (1)

119-124: Optional: Simplify IIFE to conditional expression.

The IIFE pattern works but a conditional expression would be more concise and equally declarative. I note the same pattern exists at lines 148-153, so this could be addressed together in a future cleanup.

♻️ Suggested simplification
-  const headElements = (() => {
-    if (criticalCss) {
-      return [`<style data-zpress-critical>${criticalCss}</style>`]
-    }
-    return []
-  })()
+  const headElements = criticalCss
+    ? [`<style data-zpress-critical>${criticalCss}</style>`]
+    : []
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/config.ts` around lines 119 - 124, Replace the IIFE that
builds headElements with a concise conditional expression: instead of const
headElements = (() => { if (criticalCss) { return [`<style
data-zpress-critical>${criticalCss}</style>`] } return [] })(), set headElements
using a ternary/conditional expression that returns the same array when
criticalCss is truthy and an empty array otherwise (refer to the headElements
identifier and the criticalCss variable); repeat the same simplification for the
similar pattern around the other instance currently at lines 148-153.
packages/ui/src/critical-css.ts (1)

33-40: Consider using isBuiltInTheme type guard for cleaner type narrowing.

The as BuiltInThemeName assertion bypasses type checking. Since isBuiltInTheme is already available from @zpress/theme (used in config.ts), using it here would make the validation explicit and avoid the unsafe cast.

♻️ Suggested refactor using type guard
-import type { BuiltInThemeName } from '@zpress/config'
+import type { BuiltInThemeName } from '@zpress/config'
+import { isBuiltInTheme } from '@zpress/theme'

 // ... CSS constants unchanged ...

 export function getCriticalCss(themeName: string): string {
-  const theme = themeName as BuiltInThemeName
-  const css = CRITICAL_CSS_MAP[theme]
-  if (!css) {
+  if (!isBuiltInTheme(themeName)) {
     return ''
   }
-  return css
+  return CRITICAL_CSS_MAP[themeName]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/critical-css.ts` around lines 33 - 40, The function
getCriticalCss uses an unsafe cast to BuiltInThemeName; replace the assertion
with the isBuiltInTheme type guard to validate themeName before indexing
CRITICAL_CSS_MAP. Update getCriticalCss to call isBuiltInTheme(themeName) and
only treat it as BuiltInThemeName when true, returning '' otherwise; reference
the isBuiltInTheme import from `@zpress/theme`, the CRITICAL_CSS_MAP lookup, and
the BuiltInThemeName type to guide the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/ui/src/config.ts`:
- Around line 119-124: Replace the IIFE that builds headElements with a concise
conditional expression: instead of const headElements = (() => { if
(criticalCss) { return [`<style data-zpress-critical>${criticalCss}</style>`] }
return [] })(), set headElements using a ternary/conditional expression that
returns the same array when criticalCss is truthy and an empty array otherwise
(refer to the headElements identifier and the criticalCss variable); repeat the
same simplification for the similar pattern around the other instance currently
at lines 148-153.

In `@packages/ui/src/critical-css.ts`:
- Around line 33-40: The function getCriticalCss uses an unsafe cast to
BuiltInThemeName; replace the assertion with the isBuiltInTheme type guard to
validate themeName before indexing CRITICAL_CSS_MAP. Update getCriticalCss to
call isBuiltInTheme(themeName) and only treat it as BuiltInThemeName when true,
returning '' otherwise; reference the isBuiltInTheme import from `@zpress/theme`,
the CRITICAL_CSS_MAP lookup, and the BuiltInThemeName type to guide the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae8ca80c-331d-4078-a8d3-75c54a9255b5

📥 Commits

Reviewing files that changed from the base of the PR and between ea57c61 and 61cfbcf.

📒 Files selected for processing (2)
  • packages/ui/src/config.ts
  • packages/ui/src/critical-css.ts

Fixes multiple schema and compatibility issues identified in PR review:

1. **Zod v3 compatibility**: Change to `import { z } from 'zod/v3'` for
   compatibility with zod-to-json-schema@3.25.1, which requires Zod v3
   schema objects. Redefined theme schema inline to avoid type conflicts.

2. **ConfigErrorType documentation**: Updated README to include all error
   types (missing_field, invalid_entry, invalid_section, invalid_field,
   invalid_icon, invalid_theme, duplicate_prefix, unknown).

3. **Schema strictness fixes**:
   - Changed frontmatter.head to use tuple for fixed positions:
     `z.tuple([z.string(), z.record(z.string(), z.string())])`
   - Changed outline to use tuple instead of array for fixed positions
   - Added deprecated fields (titleFrom, titleTransform) to entrySchema
     for backward compatibility with existing configs

All packages build successfully and pass type checks.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/config/src/schema.ts (1)

108-110: Good backward compatibility with deprecated fields.

Adding titleFrom and titleTransform as optional deprecated fields allows existing configs to continue validating while encouraging migration to the new title object structure. Consider adding a JSDoc @deprecated tag or runtime warning to guide users toward the new pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/config/src/schema.ts` around lines 108 - 110, The schema currently
accepts deprecated fields titleFrom and titleTransform but doesn't indicate
deprecation; add JSDoc `@deprecated` comments above the zod entries for titleFrom
and titleTransform and, in the validation path that consumes this schema (or in
a helper that normalizes config), detect when either titleFrom or titleTransform
is present and emit a runtime warning directing users to the new title object
pattern; reference the symbols titleFrom and titleTransform in your changes so
the comments and warning logic are colocated with the existing optional zod
definitions.
packages/config/README.md (1)

114-116: Consider using .map() in documentation examples for consistency.

The coding guidelines prefer .map() over .forEach() for functional-style compliance. While these are documentation examples, they serve as reference patterns for users. Consider updating to demonstrate the preferred approach.

♻️ Suggested change for line 114-116
-    error.errors?.forEach((err) => {
-      console.error(`  ${err.path.join('.')}: ${err.message}`)
-    })
+    error.errors?.map((err) => 
+      console.error(`  ${err.path.join('.')}: ${err.message}`)
+    )
♻️ Suggested change for line 396-398
-      error.errors?.forEach((err) => {
-        console.error(`  ${err.path.join('.')}: ${err.message}`)
-      })
+      error.errors?.map((err) =>
+        console.error(`  ${err.path.join('.')}: ${err.message}`)
+      )

As per coding guidelines: "Use pure, immutable, declarative TypeScript — avoid classes, loops, let, and throw; prefer map, filter, reduce".

Also applies to: 396-398

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/config/README.md` around lines 114 - 116, Replace the documentation
example that uses error.errors?.forEach(...) with a pure/functional pattern:
call error.errors?.map(err => `  ${err.path.join('.')}: ${err.message}`) to
produce an array of formatted lines, then log them in one call (e.g.,
console.error(lines.join('\n'))). Update both occurrences (the snippet using
error.errors, err.path.join and err.message at lines shown) to use map + join so
the example follows the preferred immutable/declarative style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/config/README.md`:
- Around line 114-116: Replace the documentation example that uses
error.errors?.forEach(...) with a pure/functional pattern: call
error.errors?.map(err => `  ${err.path.join('.')}: ${err.message}`) to produce
an array of formatted lines, then log them in one call (e.g.,
console.error(lines.join('\n'))). Update both occurrences (the snippet using
error.errors, err.path.join and err.message at lines shown) to use map + join so
the example follows the preferred immutable/declarative style.

In `@packages/config/src/schema.ts`:
- Around line 108-110: The schema currently accepts deprecated fields titleFrom
and titleTransform but doesn't indicate deprecation; add JSDoc `@deprecated`
comments above the zod entries for titleFrom and titleTransform and, in the
validation path that consumes this schema (or in a helper that normalizes
config), detect when either titleFrom or titleTransform is present and emit a
runtime warning directing users to the new title object pattern; reference the
symbols titleFrom and titleTransform in your changes so the comments and warning
logic are colocated with the existing optional zod definitions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81ae4511-3a3f-4637-ba2e-2c966909371f

📥 Commits

Reviewing files that changed from the base of the PR and between 61cfbcf and c388dc2.

📒 Files selected for processing (4)
  • packages/config/README.md
  • packages/config/schemas/schema.json
  • packages/config/src/errors.ts
  • packages/config/src/schema.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/config/src/errors.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant