Skip to content

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

Description

@turingcat

Reported release behavior

In the released application, setting treePathExcludePatterns to any non-empty value causes the sidebar project tree's initial scan to produce an empty tree. The same project can be scanned when the preference is an empty array.

This should be treated as a sidebar watcher/tree-construction failure, not as an invalid-glob configuration report: a pattern that matches no path must not suppress the initial tree.

Expected behavior

treePathExcludePatterns should hide only paths that match the supplied minimatch patterns. A non-matching non-empty pattern must leave the initial project tree equivalent to the empty-pattern case.

What was checked

I inspected the official v0.19.1 macOS arm64 release artifact after verifying its SHA-256 against the published checksum.

  • The packaged app.asar contains the same Watcher predicate as the v0.19.1 source tag.
  • The release bundles Chokidar 5.0.0 and Electron 42.1.0 (Node 24.15.0).
  • Running that exact bundled Chokidar in Electron's Node mode, with macOS polling enabled and a non-empty pattern that matches no path, emits the expected initial addDir and Markdown add events.
  • The renderer's project store includes the pending-event queue intended to protect the mt::update-object-tree / mt::open-directory ordering.

Those checks confirm the intended source contract but do not explain the release symptom. In particular, there is not enough evidence to claim that Chokidar, minimatch, or the packaging pipeline is the root cause.

Requested investigation

Please investigate the live application path from preference persistence through watcher creation and renderer IPC delivery. Useful instrumentation would record:

  1. The exact persisted treePathExcludePatterns value and its element types.
  2. Calls and return values of the Chokidar ignored callback during initial traversal.
  3. add/addDir events emitted by the watcher and the corresponding renderer receipt/order relative to mt::open-directory.

Until that evidence exists, please do not classify this as a glob-rule configuration error or attribute it to a particular watcher/packaging dependency.

Reproduction data still needed

The report is based on observed release behavior. I could not reproduce an empty scan in an isolated execution of the release-bundled Chokidar using a non-matching pattern. A minimal project archive, the exact preference JSON, OS/version, and whether the folder is opened at startup versus interactively would make the failing integration path reproducible.

Activity

  1. Jocs commented on Sep 19, 2026

    @Jocs
    Member

    Reproduced on develop, and it is a tree-construction failure as you suspected — not a glob-rule configuration error. Fix in #5461.

    Reproduction

    Temp project with alpha.md, beta.md, nested/gamma.md, opened through the real EditorWindow.openFolder path (watcher IPC + mt::open-directory) against a Playwright-attached build:

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

    Same on the electron-builder packaged app. So: any non-empty value, matching or not.

    The instrumentation you asked for

    1. Persisted value and element types — correct: a plain string[], exactly what the preferences text box writes via value.split(','). Never the problem.

    2. ignored callback calls and returns during initial traversal — it does not return. It throws, on the first path it is asked about:

      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._userIgnored ([email protected]/index.js:76:20)
          at FSWatcher._isIgnored ([email protected]/index.js:668:21)
          at NodeFsHandler._addToNodeFs ([email protected]/handler.js:583:26)
      
    3. add/addDir events and renderer receipt order — none are emitted. chokidar raises error instead of traversing, so nothing ever reaches mt::update-object-tree. The renderer's pending-event queue and the mt::open-directory ordering are both fine; there is simply nothing to queue.

    Root cause

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

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

    so the named import is undefined.

    It stayed invisible because src/types/shims.d.ts hand-declared the module with an export that does not exist:

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

    typecheck and lint therefore pass over a binding that can never exist at runtime.

    checkPathExcludePattern only reaches the call inside its pattern loop, which is why the empty array works and every non-empty value fails — the loop body is the first and only thing that touches minimatch.

    Your isolated test was right to clear chokidar and minimatch individually: each is fine on its own. The break is the module boundary between them, which only exists in the app's own bundle.

    Fix

    #5461 declares minimatch as a real dependency and removes the shim so the package's own types apply. A side effect worth flagging: treePathExcludePatterns now filters for the first time since #4001 — before this it either did nothing (empty) or blanked the tree (non-empty).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions