diff --git a/lib/path-watcher.js b/lib/path-watcher.js index 45ec0c2e..e172e140 100644 --- a/lib/path-watcher.js +++ b/lib/path-watcher.js @@ -241,6 +241,18 @@ class PathWatcher { return true } + const shouldRewrite = !this.watchedPath.startsWith(this.normalizedPath) + const modifyPath = shouldRewrite + ? eventPath => this.watchedPath + eventPath.substring(this.normalizedPath.length) + : eventPath => eventPath + const modifyEvent = shouldRewrite + ? event => { + const e = {action: event.action, kind: event.kind, path: modifyPath(event.path)} + if (event.oldPath !== undefined) e.oldPath = modifyPath(event.oldPath) + return e + } + : event => event + const filtered = [] for (let i = 0; i < events.length; i++) { const event = events[i] @@ -250,15 +262,15 @@ class PathWatcher { const destWatched = isWatchedPath(event.path) if (srcWatched && destWatched) { - filtered.push(event) + filtered.push(modifyEvent(event)) } else if (srcWatched && !destWatched) { - filtered.push({action: 'deleted', kind: event.kind, path: event.oldPath}) + filtered.push({action: 'deleted', kind: event.kind, path: modifyPath(event.oldPath)}) } else if (!srcWatched && destWatched) { - filtered.push({action: 'created', kind: event.kind, path: event.path}) + filtered.push({action: 'created', kind: event.kind, path: modifyPath(event.path)}) } } else { if (isWatchedPath(event.path)) { - filtered.push(event) + filtered.push(modifyEvent(event)) } } } diff --git a/test/events/symlink.test.js b/test/events/symlink.test.js new file mode 100644 index 00000000..a92fa908 --- /dev/null +++ b/test/events/symlink.test.js @@ -0,0 +1,44 @@ +const fs = require('fs-extra') + +const {Fixture} = require('../helper') +const {EventMatcher} = require('../matcher') + +describe('watching beneath symlinked directories', function () { + let fixture + + beforeEach(async function () { + fixture = new Fixture() + await fixture.before() + await fixture.log() + }) + + afterEach(async function () { + await fixture.after(this.currentTest) + }) + + it('reports paths consistently with the argument to watchPath', async function () { + const realSubdir = fixture.watchPath('realdir') + const realFile = fixture.watchPath('realdir', 'file.txt') + + const symlinkSubdir = fixture.watchPath('linkdir') + const symlinkFile = fixture.watchPath('linkdir', 'file.txt') + + await fs.mkdirs(realSubdir) + await fs.symlink(realSubdir, symlinkSubdir) + + const symlinkMatcher = new EventMatcher(fixture) + const sw = await symlinkMatcher.watch(['linkdir'], {}) + + const realMatcher = new EventMatcher(fixture) + const rw = await realMatcher.watch(['realdir'], {}) + + assert.strictEqual(sw.native, rw.native) + + await fs.writeFile(realFile, 'contents\n') + + await Promise.all([ + until('symlink event arrives', symlinkMatcher.allEvents({action: 'created', kind: 'file', path: symlinkFile})), + until('real path event arrives', realMatcher.allEvents({action: 'created', kind: 'file', path: realFile})) + ]) + }) +})