PulsarNext: Filesystem changes within symlinked folders no longer detected on macOS
Thanks in advance for your bug report!
- [x] Have you reproduced issue in safe mode?
- [x] Have you used the debugging guide to try to resolve the issue?
- [x] Have you checked our FAQs to make sure your question isn't answered there?
- [x] Have you checked to make sure your issue does not already exist?
- [x] Have you checked you are on the latest release of Pulsar?
What happened?
A user on Discord reports that PulsarNext has regressed in its file-watching capabilities, and can no longer detect changes to files outside of Pulsar if those files are within symlinked folders.
Pulsar version
PulsarNext 1.126.2025032416-next
Which OS does this happen on?
🍎 macOS
OS details
both Ventura and Sequoia
Which CPU architecture are you running this on?
ARM64/AArch64
What steps are needed to reproduce this?
mkdir project mkdir project/folder ln -s folder project/folder_symlink pulsar-next projectIn Pulsar Tree View expand all project folders
mkdir project/folder/testNow in Tree View new folder
testappears inproject/folderbut not inproject/folder_symlink
Additional Information:
This is interesting. It might actually be an issue with nsfw, since PulsarNext recently attempted to switch off of node-pathwatcher and onto Pulsar’s builtin, nsfw-based watchPath function. Might just be a configuration thing, since I'm sure we're not the first people with this use case in mind.
Having trouble reproducing this myself, and my system is very similar to that of the reporter. I suspect the repro steps are more nuanced than what's written above, since the reporter is experiencing this in a messy real-world scenario.
I'll keep it open for now; hopefully we can pin down the exact scenarios under which it's happening.
Just in case attaching screen recording: https://github.com/user-attachments/assets/a6802a0d-932d-4b41-b50a-3d93c6e9dd6b
Yeah, those same steps fail to reproduce the issue for me.
Soon there'll be a new ARM64 release of PulsarNext for you to try, but I don't have any reason to believe this behavior changed.
tree-view used to use node-pathwatcher, but I switched it to watchPath as part of an effort to deprecate node-pathwatcher. (We don't want to maintain our own filesystem watcher.) watchPath uses nsfw internally; nsfw is widely used in the Node ecosystem, but it apparently doesn't support some symlinking scenarios.
This shouldn't end up mattering, though! Here's why:
-
node-pathwatcherdidn't support recursive watching — meaning it could watch a directory, but only for changes to the directory's children. Meaning… - …each directory item in
tree-viewis responsible for monitoring its own children and sets up its own file-watcher for doing so. - This general architecture was preserved even when I switched over to
watchPath. -
watchPathsets up recursive watchers by default and tries very hard to reuse existing watchers, so it would be plausible for it to think “didn't we already set up a watcher on the parent? I should just be able to reuse that!” That would be disastrous if the child directory is symlinked. Except… - the watcher resolves symlinks early in the process! Since it would see the directory's real path on disk, it wouldn't think it could reuse an existing watcher. So everything should theoretically work.
But the fact that it works for me but not for @sergeyoleynik means I don't know whether this logic is sound. A couple things could be happening:
- Despite what I wrote above,
watchPathcould be attempting to reuse a watcher that it shouldn't. (No theory yet on why it would only do this some of the time.) - Because these repro steps involve the real directory and the symlinked directory existing within the project, maybe the watcher is firing events, but the instance of
Directorythat manages the symlinked directory doesn't think it should reload. Maybe this code path isn't being hit, but it would pass if we checkedthis.realPathin filePathIsChildOfDirectory instead ofthis.path. (@sergeyoleynik, if you're handy in Chrome developer tools, you could set some breakpoints and test this out yourself.)
@savetheclocktower Thank you very much, you was right.
Everything starts to work as expected after i changed this.path to this.realPath in this line in debugger
Also there is one more file system issue. I have Apache/2.4.63 on my machine and local host website for development purposes. There is Apache log file in PulsarNext project folder and when Apache writes logs to this file, Pulsar not refreshing file contents in text editor. So i need to reopen this file each time to update logs. Maybe you have any ideas why this file not refreshing? 🙏
The strange thing is — if i update this file manually in any other text editor, PulsarNext works fine and in editor everything updates correctly.
@savetheclocktower Thank you very much, you was right. Everything starts to work as expected after i changed
this.pathtothis.realPathin this line in debugger
Fantastic! Still no idea why our machines behave differently, but I can make this change pretty easily.
Also there is one more file system issue. I have Apache/2.4.63 on my machine and local host website for development purposes. There is Apache log file in PulsarNext project folder and when Apache writes logs to this file, Pulsar not refreshing file contents in text editor. So i need to reopen this file each time to update logs. Maybe you have any ideas why this file not refreshing? 🙏
The strange thing is — if i update this file manually in any other text editor, PulsarNext works fine and in editor everything updates correctly.
This is a curious one. The core change detection in the editor still uses node-pathwatcher (I'm pretty sure). The old version of that library (still being used for Pulsar stable) uses an old BSD system called kqueue to detect filesystem events on macOS, but the rewritten version (used for PulsarNext) modernizes the file-watching approach and uses the standard FSEvents API. It wouldn't surprise me that behavior differences result from the change, but if anything I would've expected the new implementation to be more accurate.
This code is another candidate for conversion to watchPath/nsfw… but nsfw uses FSEvents on macOS just like we do!
@sergeyoleynik, if you've got an editor like VS Code installed, let me know if it keeps up to date on a log file when Apache writes updates. If VS Code can detect it, it ought to be within our capabilities to detect it as well.
@savetheclocktower Yes, i've checked in VS Code just now, it updates Apache log file correctly without any issues 🤷♂️ Unfortunately i'm not so good in programming and i can't even find place in debugger where file update event listener is located to check what happens when Apache writes to file. I would be grateful if you give me any ideas — where to search 🥹
UPD: I did little investigation — fs.watch does not handle any event when those log files are changing. While fs.watchFile works fine
@savetheclocktower Yes, i've checked in VS Code just now, it updates Apache log file correctly without any issues 🤷♂️ Unfortunately i'm not so good in programming and i can't even find place in debugger where file update event listener is located to check what happens when Apache writes to file. I would be grateful if you give me any ideas — where to search 🥹
UPD: I did little investigation — fs.watch does not handle any event when those log files are changing. While fs.watchFile works fine
Sorry I haven't been able to chase this down yet, but I haven't forgotten about it. I'll keep this ticket open as a reminder to fix that bug in tree-view.
I think the code that would detect file changes for your log file lives somewhere within the text-buffer package, but I can't be sure until I look for myself. And it would almost certainly still use node-pathwatcher to detect the file change.
@savetheclocktower Thank you. I've tried to listen access.log require('pathwatcher').watch('access.log',(e)=>{console.log(e)}) and nothing happened when Apache wrote to log. When i edit access.log with any text editor, callback function is working. It seems like the problem in pathwatcher module