From b5e037f7e75c7caa320ec7e02dc8fd57ff45e205 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Thu, 7 Dec 2023 21:22:45 +0300 Subject: [PATCH] v1.5.0 --- CHANGELOG.md | 5 ++++ index.js | 81 ++++++++++++++++++++++++++++++++++------------------ lib/index.js | 8 +++--- package.json | 2 +- 4 files changed, 64 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f042b9e..4f113b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased][unreleased] +## [1.5.0][] - 2023-12-07 + +- Code perfomance improvements +- Symbolic keywords for isolation + ## [1.4.0][] - 2023-12-05 - Code quality improvements diff --git a/index.js b/index.js index b4b32bd..bf64b94 100644 --- a/index.js +++ b/index.js @@ -1,46 +1,73 @@ 'use strict'; -const { access: accessible, scheduler } = require('./lib'); +const { access, scheduler } = require('./lib'); const { stat, readdir, watch } = require('node:fs'); const { EventEmitter } = require('node:events'); const { join, sep } = require('node:path'); module.exports = Watcher; module.exports.default = Watcher; -const READ_OPTS = { withFileTypes: true }; Object.setPrototypeOf(Watcher.prototype, EventEmitter.prototype); +const READ_OPTS = { withFileTypes: true }; +const kWatchers = Symbol('watchers'); +const kListener = Symbol('listener'); +const kOptions = Symbol('options'); +const kLookup = Symbol('lookup'); +const kEmit = Symbol('emit'); + function Watcher(options = {}) { if (!new.target) return new Watcher(options); const { timeout, ignore = [], home, deep } = options; - const access = accessible.bind(null, [...ignore]); + EventEmitter.call(this); + this[kWatchers] = new Map(); + this[kEmit] = scheduler(this, timeout); + this[kOptions] = { timeout, ignore, home, deep }; +} + +Watcher.prototype.watch = function (path) { + const { [kWatchers]: watchers, [kOptions]: options } = this; + const { deep, ignore } = options; + if (watchers.has(path) || !access(ignore, path)) return this; + stat(path, (err, stats) => { + if (err || watchers.has(path)) return; + watchers.set(path, watch(path, this[kListener](path))); + stats.isDirectory() && deep && readdir(path, READ_OPTS, this[kLookup](path)); + }); + return this; +}; + +Watcher.prototype.unwatch = function (path) { + const watchers = this[kWatchers]; + watchers.get(path)?.close(), watchers.delete(path); + return this; +}; - const lookup = path => (err, files) => - void (!err && files.forEach(f => f.isDirectory() && this.watch(join(path, f.name)))); +Watcher.prototype.close = function () { + this.clear(), this.removeAllListeners(); + return this; +}; - const emit = scheduler(this.emit.bind(this), timeout); - const listener = path => (_, filename) => { +Watcher.prototype.clear = function () { + const watchers = this[kWatchers]; + watchers.forEach(watcher => void watcher.close()); + return watchers.clear(), this; +}; + +Watcher.prototype[kLookup] = function (path) { + const lookup = file => void (file.isDirectory() && this.watch(join(path, file.name))); + return (err, files) => void (!err && files.forEach(lookup)); +}; + +Watcher.prototype[kListener] = function (path) { + const { home, deep, ignore } = this[kOptions]; + return (_, filename) => { const target = path.endsWith(sep + filename) ? path : join(path, filename); - if (!access(target)) return; + if (!access(ignore, target)) return; stat(target, (err, stats) => { const parsed = home ? target.replace(home, '') : target; - if (err) return void (this.unwatch(target), emit('delete', parsed)); - stats.isDirectory() && deep && readdir(target, READ_OPTS, lookup(path)); - return void emit('change', parsed); + if (err) return void (this.unwatch(target), this[kEmit]('delete', parsed)); + stats.isDirectory() && deep && readdir(target, READ_OPTS, this[kLookup](path)); + return void this[kEmit]('change', parsed); }); }; - - const watchers = new Map(); - EventEmitter.call(this); - this.close = () => (this.clear(), this.removeAllListeners(), this); - this.clear = () => (watchers.forEach(watcher => void watcher.close()), watchers.clear(), this); - this.unwatch = path => (watchers.get(path)?.close(), watchers.delete(path), this); - this.watch = path => { - if (watchers.has(path) || !access(path)) return this; - stat(path, (err, stats) => { - if (err || watchers.has(path)) return; - watchers.set(path, watch(path, listener(path))); - stats.isDirectory() && deep && readdir(path, READ_OPTS, lookup(path)); - }); - return this; - }; -} +}; diff --git a/lib/index.js b/lib/index.js index 2dc1dfb..db1d58d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,7 +7,7 @@ const access = (list, v) => { return flag; }; -const scheduler = (emit, timeout = TIMEOUT) => { +const scheduler = (watcher, timeout = TIMEOUT) => { var timer = null; const queue = new Map(); const sendQueue = () => { @@ -15,9 +15,9 @@ const scheduler = (emit, timeout = TIMEOUT) => { timer = (clearTimeout(timer), null); const packet = [...queue.entries()]; queue.clear(); - emit('before', packet); - packet.forEach(({ 0: path, 1: event }) => emit(event, path)); - emit('after', packet); + watcher.emit('before', packet); + packet.forEach(({ 0: path, 1: event }) => watcher.emit(event, path)); + watcher.emit('after', packet); }; return (event, path) => { diff --git a/package.json b/package.json index 507dba7..14c2889 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "license": "MIT", - "version": "1.4.0", + "version": "1.5.0", "type": "commonjs", "name": "filesnitch", "homepage": "https://astrohelm.ru",