Skip to content

fix(desktop): restore treePathExcludePatterns filtering in the sidebar tree - #5461

Merged
Jocs merged 1 commit into
developfrom
fix/5159-tree-exclude-patterns-minimatch
Sep 19, 2026
Merged

Jocs merged 1 commit into
developfrom
fix/5159-tree-exclude-patterns-minimatch

Conversation

@Jocs

@Jocs Jocs commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Fixes #5159.

Reproduced

On develop, with a temp project holding alpha.md, beta.md and nested/gamma.md, driving the real EditorWindow.openFolder path (watcher IPC + mt::open-directory) in a Playwright-attached build:

treePathExcludePatterns before after
[] alpha.md, beta.md, nested alpha.md, beta.md, nested
["*.no-such-ext"] (matches nothing) empty tree alpha.md, beta.md, nested
["beta.md"] empty tree alpha.md, nested (beta.md hidden)

The main process logs, once per traversal step:

Error while watching files: TypeError: minimatchExports.minimatch is not a function
    at checkPathExcludePattern (out/main/index.js:1560:26)
    at ignored (out/main/index.js:5672:13)
    at matchPatterns ([email protected]/index.js:61:13)
    at FSWatcher._isIgnored ([email protected]/index.js:668:21)
    at NodeFsHandler._addToNodeFs ([email protected]/handler.js:583:26)

Same result against the electron-builder packaged .app.

Root cause

packages/desktop/src/common/filesystem/paths.ts imports { minimatch } from 'minimatch', but minimatch was never a declared dependency — it has been a phantom dependency since #4001 introduced the import. With shamefully-hoist=true it resolved to whichever transitive copy won the root node_modules hoist, here minimatch@3, whose CommonJS export is the function itself:

module.exports = minimatch
minimatch.Minimatch = Minimatch
// no `minimatch.minimatch`

so the named import was undefined.

packages/desktop/src/types/shims.d.ts hand-declared the module:

declare module 'minimatch' {
  export function minimatch(target: string, pattern: string, options?: unknown): boolean
}

which is why typecheck and lint stayed green over a binding that could never exist at runtime.

checkPathExcludePattern only reaches the call inside its pattern loop, so an empty array short-circuits and works, while any non-empty value throws TypeError: minimatch is not a function. The sidebar watcher calls it from chokidar's ignored predicate, where the throw aborts the initial traversal — chokidar emits error instead of the addDir/add events the renderer's project store builds the tree from, so the sidebar stays empty.

This matches the report's note that isolating the release-bundled chokidar did not reproduce it: chokidar is fine, and so is minimatch — the break is the module boundary between them.

Fix

Declare minimatch as a real dependency of packages/desktop and drop the shim so the package's own types apply. minimatch@9 ships dual CJS/ESM with a real named export, and require("minimatch").minimatch is what the bundled main process now calls. (Pinned to 9 rather than 10: minimatch 10 relicensed to BlueOak-1.0.0, which validate-licenses rejects. 9.0.9 is still ISC.)

Side effect worth noting: treePathExcludePatterns filters for the first time since #4001 — previously it either did nothing (empty) or emptied the tree (non-empty).

Verification

  • New unit spec path-exclude-pattern.spec.ts — fails on develop with TypeError: minimatch is not a function on every non-empty pattern case.
  • pnpm test:unit — 826 passed / 61 files.
  • pnpm typecheck, pnpm lint — clean (0 errors).
  • electron-builder --dir: minimatch present under app.asar/node_modules/minimatch, and the packaged app filters correctly.
  • pnpm validate-licenses — clean.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Website preview ready: https://pr-5461-marktext-website.ransixi.workers.dev

Built from 39fd11cd4b812de09ce962f2996ad28dc2fecdf6 · Worker: marktext-website · Version: 8c5a21b5-28f2-4625-88c3-ab7123dfae49 · Alias: pr-5461

…r tree (#5159)

Any non-empty `treePathExcludePatterns` emptied the sidebar project tree.

`common/filesystem/paths.ts` imports `{ minimatch } from 'minimatch'`, but
minimatch was never declared as a dependency. With `shamefully-hoist=true`
it resolved to whichever transitive copy landed in the root node_modules —
minimatch@3, whose CommonJS export is the function itself and carries no
named `minimatch` export. So the imported binding was `undefined`.

`src/types/shims.d.ts` hand-declared `export function minimatch(...)` for
the module, which is why typecheck and lint stayed green over a binding
that could never exist at runtime.

`checkPathExcludePattern` only reaches the call inside the pattern loop, so
an empty array worked and any non-empty one threw
`TypeError: minimatch is not a function`. The watcher calls it from
chokidar's `ignored` predicate, where the throw aborts the initial
traversal: chokidar emits `error` instead of the `addDir`/`add` events the
renderer builds the tree from, leaving the sidebar empty.

Declare minimatch as a real dependency and drop the shim so the module's
own types apply. Path exclusion now works as documented — it had been
inert since #4001 introduced the import.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@Jocs
Jocs force-pushed the fix/5159-tree-exclude-patterns-minimatch branch from 5385ed4 to 39fd11c Compare September 19, 2026 14:39
@github-actions

Copy link
Copy Markdown

Build artifacts for PR #5461:

Run: https://github.com/marktext/marktext/actions/runs/35449427513

Artifact Size Link
marktext-linux 638.4 MB Download
marktext-macos-arm64 287.5 MB Download
marktext-windows-arm64 279.5 MB Download
marktext-macos-x64 297.8 MB Download
marktext-windows-x64 287.6 MB Download

@Jocs
Jocs merged commit 5294c70 into develop Sep 19, 2026
19 checks passed
@Jocs
Jocs deleted the fix/5159-tree-exclude-patterns-minimatch branch September 19, 2026 15:06
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.

bug: non-empty treePathExcludePatterns can leave initial project tree empty

1 participant