Comparing version 0.11.0 to 0.11.1
@@ -0,1 +1,5 @@ | ||
# v0.11.1 | ||
- Close FSWatcher instances when their directories are deleted | ||
# v0.11.0 | ||
@@ -2,0 +6,0 @@ |
@@ -14,6 +14,11 @@ 'use strict'; | ||
constructor(path$$1) { | ||
// path of file | ||
this._path = path$$1; | ||
// all historical paths of file | ||
this.paths = path$$1 ? [path$$1] : []; | ||
// stat of file | ||
this.stat = null; | ||
// Buffer of file contents | ||
this._bytes = null; | ||
// string of file contents | ||
this._text = null; | ||
@@ -78,14 +83,22 @@ } | ||
super(); | ||
// directory to recursively watch the contents of | ||
this._dir = dir; | ||
// whether to actually watch for changes (or just walk and retrieve contents and file stats) | ||
this._watch = watch; | ||
// fs.watch event debounce, in milliseconds | ||
this._debounce = debounce; | ||
this._dirs = new Set(); | ||
// paths of all (recursive) directories -> FSWatcher instances | ||
this._dirs = new Map(); | ||
// paths of all (recursive) files -> file stats | ||
this._files = new Map(); | ||
// paths of (recursive) files with pending debounced events -> setTimeout timer ids | ||
this._timeouts = new Map(); | ||
// queue of pending FSWatcher events to handle | ||
this._queue = []; | ||
// whether some FSWatcher event is currently already in the process of being handled | ||
this._processing = false; | ||
this._queue = []; | ||
} | ||
// recurse directroy, get stats, set up FSWatcher instances | ||
// returns array of { file, stat } | ||
async init() { | ||
@@ -96,2 +109,3 @@ await this._recurse(this._dir); | ||
// recurse a given directory | ||
async _recurse(full) { | ||
@@ -103,16 +117,3 @@ let path$$1 = full.slice(this._dir.length + 1); | ||
} else if (fileStat.isDirectory()) { | ||
if (this._watch) { | ||
this._dirs.add(path$$1); | ||
fs.watch(full, (_, file) => { | ||
file = full + '/' + file; | ||
if (this._timeouts.has(file)) clearTimeout(this._timeouts.get(file)); | ||
this._timeouts.set( | ||
file, | ||
setTimeout(() => { | ||
this._timeouts.delete(file); | ||
this._enqueue(file); | ||
}, this._debounce), | ||
); | ||
}); | ||
} | ||
if (this._watch) this._dirs.set(path$$1, fs.watch(full, this._handle.bind(this, full))); | ||
await Promise.all((await readdir(full)).map(sub => this._recurse(full + '/' + sub))); | ||
@@ -122,2 +123,16 @@ } | ||
// handle FSWatcher event for given directory | ||
_handle(dir, event, file) { | ||
let full = dir + '/' + file; | ||
if (this._timeouts.has(full)) clearTimeout(this._timeouts.get(full)); | ||
this._timeouts.set( | ||
full, | ||
setTimeout(() => { | ||
this._timeouts.delete(full); | ||
this._enqueue(full); | ||
}, this._debounce), | ||
); | ||
} | ||
// add an FSWatcher event to the queue, and handle queued events | ||
async _enqueue(full) { | ||
@@ -128,3 +143,3 @@ this._queue.push(full); | ||
while (this._queue.length) { | ||
full = this._queue.shift(); | ||
let full = this._queue.shift(); | ||
let path$$1 = full.slice(this._dir.length + 1); | ||
@@ -134,5 +149,7 @@ try { | ||
if (fileStat.isFile()) { | ||
// note the new/changed file | ||
this._files.set(path$$1, fileStat); | ||
this.emit('', { event: '+', path: path$$1, stat: fileStat }); | ||
} else if (fileStat.isDirectory() && !this._dirs.has(path$$1)) { | ||
// note the new directory: start watching it, and report any files in it | ||
await this._recurse(full); | ||
@@ -146,7 +163,15 @@ for (let [newPath, fileStat] of this._files.entries()) { | ||
} catch (e) { | ||
// probably this was a deleted file/directory | ||
if (this._files.has(path$$1)) { | ||
// note the deleted file | ||
this._files.delete(path$$1); | ||
this.emit('', { event: '-', path: path$$1 }); | ||
} else if (this._dirs.has(path$$1)) { | ||
this._dirs.delete(path$$1); | ||
// note the deleted directory: stop watching it, and report any files that were in it | ||
for (let old of this._dirs.keys()) { | ||
if (old === path$$1 || old.startsWith(path$$1 + '/')) { | ||
this._dirs.get(old).close(); | ||
this._dirs.delete(old); | ||
} | ||
} | ||
for (let old of this._files.keys()) { | ||
@@ -158,3 +183,2 @@ if (old.startsWith(path$$1 + '/')) { | ||
} | ||
for (let old of this._dirs.keys()) if (old.startsWith(path$$1 + '/')) this._dirs.delete(old); | ||
} | ||
@@ -176,3 +200,3 @@ } | ||
this._status = null; | ||
// all registered watchers (one per directory) | ||
// all registered Watcher instances (one per directory) | ||
this._watchers = []; | ||
@@ -183,10 +207,6 @@ // all registered transforms | ||
this._generators = new Map(); | ||
// how many promises we're currently waiting to resolve as part of the first exec wave | ||
this._count = 0; | ||
// resolve main exec promise | ||
this._res = null; | ||
// reject main exec promise | ||
this._rej = null; | ||
// original paths of all files currently undergoing some transform | ||
this._transforming = new Set(); | ||
// { count, resolve, reject } object for main promise for the first exec wave | ||
this._done = { count: 0, resolve: null, reject: null }; | ||
// original paths of all files currently undergoing some transform/generator | ||
this._pending = new Set(); | ||
// original paths -> number of other files they're currently waiting on to exist | ||
@@ -198,5 +218,5 @@ this._waiting = new Map(); | ||
this._dependents = new Map(); | ||
// queue of pending watcher events to handle | ||
// queue of pending Watcher events to handle | ||
this._queue = []; | ||
// where some watcher event is currently already in the process of being handled | ||
// whether some Watcher event is currently already in the process of being handled | ||
this._processing = false; | ||
@@ -225,3 +245,3 @@ } | ||
// register one or more directories/watchers | ||
// register one or more directories/Watchers | ||
dir(...dirs) { | ||
@@ -260,6 +280,6 @@ this._checkBeforeExec('dir'); | ||
let promise = new Promise((res, rej) => { | ||
this._res = res; | ||
this._rej = rej; | ||
this._done.resolve = res; | ||
this._done.reject = rej; | ||
}); | ||
// init all watchers; note that all files have pending transforms | ||
// init all Watcher instances; note that all files have pending transforms | ||
let files = []; | ||
@@ -274,7 +294,7 @@ await Promise.all( | ||
this._origFiles.set(path$$1, null); | ||
this._transforming.add(path$$1); | ||
this._pending.add(path$$1); | ||
} | ||
}), | ||
); | ||
for (let path$$1 of this._generators.keys()) this._transforming.add(path$$1); | ||
for (let path$$1 of this._generators.keys()) this._pending.add(path$$1); | ||
// process each physical file | ||
@@ -329,4 +349,4 @@ for (let { dir, path: path$$1, stat: stat$$1, read } of files) { | ||
if (!this._status) { | ||
this._count++; | ||
promise.then(() => --this._count || this._res(), this._rej); | ||
this._done.count++; | ||
promise.then(() => --this._done.count || this._done.resolve(), this._done.reject); | ||
} | ||
@@ -336,3 +356,3 @@ return promise | ||
// add a watcher event from the queue, and handle queued events | ||
// add a Watcher event to the queue, and handle queued events | ||
async _enqueue(event) { | ||
@@ -379,3 +399,3 @@ this._queue.push(event); | ||
let { path: path$$1 } = file; | ||
this._transforming.add(path$$1); | ||
this._pending.add(path$$1); | ||
try { | ||
@@ -393,7 +413,7 @@ for (let transform of this._transforms) { | ||
} | ||
this._transforming.delete(path$$1); | ||
if (!this._status && [...this._transforming].every(path$$1 => this._waiting.get(path$$1))) { | ||
// all pending transforming files are currently waiting for one or more other files to exist | ||
this._pending.delete(path$$1); | ||
if (!this._status && [...this._pending].every(path$$1 => this._waiting.get(path$$1))) { | ||
// all pending files are currently waiting for one or more other files to exist | ||
// break deadlock: assume all files that have not appeared yet will never do so | ||
for (let path$$1 of this._available.keys()) if (!this._transforming.has(path$$1)) this._found(path$$1); | ||
for (let path$$1 of this._available.keys()) if (!this._pending.has(path$$1)) this._found(path$$1); | ||
} | ||
@@ -400,0 +420,0 @@ } |
@@ -8,6 +8,11 @@ import { readdir, readFile, stat, watch } from 'fs'; | ||
constructor(path) { | ||
// path of file | ||
this._path = path; | ||
// all historical paths of file | ||
this.paths = path ? [path] : []; | ||
// stat of file | ||
this.stat = null; | ||
// Buffer of file contents | ||
this._bytes = null; | ||
// string of file contents | ||
this._text = null; | ||
@@ -72,14 +77,22 @@ } | ||
super(); | ||
// directory to recursively watch the contents of | ||
this._dir = dir; | ||
// whether to actually watch for changes (or just walk and retrieve contents and file stats) | ||
this._watch = watch$$1; | ||
// fs.watch event debounce, in milliseconds | ||
this._debounce = debounce; | ||
this._dirs = new Set(); | ||
// paths of all (recursive) directories -> FSWatcher instances | ||
this._dirs = new Map(); | ||
// paths of all (recursive) files -> file stats | ||
this._files = new Map(); | ||
// paths of (recursive) files with pending debounced events -> setTimeout timer ids | ||
this._timeouts = new Map(); | ||
// queue of pending FSWatcher events to handle | ||
this._queue = []; | ||
// whether some FSWatcher event is currently already in the process of being handled | ||
this._processing = false; | ||
this._queue = []; | ||
} | ||
// recurse directroy, get stats, set up FSWatcher instances | ||
// returns array of { file, stat } | ||
async init() { | ||
@@ -90,2 +103,3 @@ await this._recurse(this._dir); | ||
// recurse a given directory | ||
async _recurse(full) { | ||
@@ -97,16 +111,3 @@ let path = full.slice(this._dir.length + 1); | ||
} else if (fileStat.isDirectory()) { | ||
if (this._watch) { | ||
this._dirs.add(path); | ||
watch(full, (_, file) => { | ||
file = full + '/' + file; | ||
if (this._timeouts.has(file)) clearTimeout(this._timeouts.get(file)); | ||
this._timeouts.set( | ||
file, | ||
setTimeout(() => { | ||
this._timeouts.delete(file); | ||
this._enqueue(file); | ||
}, this._debounce), | ||
); | ||
}); | ||
} | ||
if (this._watch) this._dirs.set(path, watch(full, this._handle.bind(this, full))); | ||
await Promise.all((await readdir$1(full)).map(sub => this._recurse(full + '/' + sub))); | ||
@@ -116,2 +117,16 @@ } | ||
// handle FSWatcher event for given directory | ||
_handle(dir, event, file) { | ||
let full = dir + '/' + file; | ||
if (this._timeouts.has(full)) clearTimeout(this._timeouts.get(full)); | ||
this._timeouts.set( | ||
full, | ||
setTimeout(() => { | ||
this._timeouts.delete(full); | ||
this._enqueue(full); | ||
}, this._debounce), | ||
); | ||
} | ||
// add an FSWatcher event to the queue, and handle queued events | ||
async _enqueue(full) { | ||
@@ -122,3 +137,3 @@ this._queue.push(full); | ||
while (this._queue.length) { | ||
full = this._queue.shift(); | ||
let full = this._queue.shift(); | ||
let path = full.slice(this._dir.length + 1); | ||
@@ -128,5 +143,7 @@ try { | ||
if (fileStat.isFile()) { | ||
// note the new/changed file | ||
this._files.set(path, fileStat); | ||
this.emit('', { event: '+', path, stat: fileStat }); | ||
} else if (fileStat.isDirectory() && !this._dirs.has(path)) { | ||
// note the new directory: start watching it, and report any files in it | ||
await this._recurse(full); | ||
@@ -140,7 +157,15 @@ for (let [newPath, fileStat] of this._files.entries()) { | ||
} catch (e) { | ||
// probably this was a deleted file/directory | ||
if (this._files.has(path)) { | ||
// note the deleted file | ||
this._files.delete(path); | ||
this.emit('', { event: '-', path }); | ||
} else if (this._dirs.has(path)) { | ||
this._dirs.delete(path); | ||
// note the deleted directory: stop watching it, and report any files that were in it | ||
for (let old of this._dirs.keys()) { | ||
if (old === path || old.startsWith(path + '/')) { | ||
this._dirs.get(old).close(); | ||
this._dirs.delete(old); | ||
} | ||
} | ||
for (let old of this._files.keys()) { | ||
@@ -152,3 +177,2 @@ if (old.startsWith(path + '/')) { | ||
} | ||
for (let old of this._dirs.keys()) if (old.startsWith(path + '/')) this._dirs.delete(old); | ||
} | ||
@@ -170,3 +194,3 @@ } | ||
this._status = null; | ||
// all registered watchers (one per directory) | ||
// all registered Watcher instances (one per directory) | ||
this._watchers = []; | ||
@@ -177,10 +201,6 @@ // all registered transforms | ||
this._generators = new Map(); | ||
// how many promises we're currently waiting to resolve as part of the first exec wave | ||
this._count = 0; | ||
// resolve main exec promise | ||
this._res = null; | ||
// reject main exec promise | ||
this._rej = null; | ||
// original paths of all files currently undergoing some transform | ||
this._transforming = new Set(); | ||
// { count, resolve, reject } object for main promise for the first exec wave | ||
this._done = { count: 0, resolve: null, reject: null }; | ||
// original paths of all files currently undergoing some transform/generator | ||
this._pending = new Set(); | ||
// original paths -> number of other files they're currently waiting on to exist | ||
@@ -192,5 +212,5 @@ this._waiting = new Map(); | ||
this._dependents = new Map(); | ||
// queue of pending watcher events to handle | ||
// queue of pending Watcher events to handle | ||
this._queue = []; | ||
// where some watcher event is currently already in the process of being handled | ||
// whether some Watcher event is currently already in the process of being handled | ||
this._processing = false; | ||
@@ -219,3 +239,3 @@ } | ||
// register one or more directories/watchers | ||
// register one or more directories/Watchers | ||
dir(...dirs) { | ||
@@ -254,6 +274,6 @@ this._checkBeforeExec('dir'); | ||
let promise = new Promise((res, rej) => { | ||
this._res = res; | ||
this._rej = rej; | ||
this._done.resolve = res; | ||
this._done.reject = rej; | ||
}); | ||
// init all watchers; note that all files have pending transforms | ||
// init all Watcher instances; note that all files have pending transforms | ||
let files = []; | ||
@@ -268,7 +288,7 @@ await Promise.all( | ||
this._origFiles.set(path, null); | ||
this._transforming.add(path); | ||
this._pending.add(path); | ||
} | ||
}), | ||
); | ||
for (let path of this._generators.keys()) this._transforming.add(path); | ||
for (let path of this._generators.keys()) this._pending.add(path); | ||
// process each physical file | ||
@@ -323,4 +343,4 @@ for (let { dir, path, stat: stat$$1, read } of files) { | ||
if (!this._status) { | ||
this._count++; | ||
promise.then(() => --this._count || this._res(), this._rej); | ||
this._done.count++; | ||
promise.then(() => --this._done.count || this._done.resolve(), this._done.reject); | ||
} | ||
@@ -330,3 +350,3 @@ return promise | ||
// add a watcher event from the queue, and handle queued events | ||
// add a Watcher event to the queue, and handle queued events | ||
async _enqueue(event) { | ||
@@ -373,3 +393,3 @@ this._queue.push(event); | ||
let { path } = file; | ||
this._transforming.add(path); | ||
this._pending.add(path); | ||
try { | ||
@@ -387,7 +407,7 @@ for (let transform of this._transforms) { | ||
} | ||
this._transforming.delete(path); | ||
if (!this._status && [...this._transforming].every(path => this._waiting.get(path))) { | ||
// all pending transforming files are currently waiting for one or more other files to exist | ||
this._pending.delete(path); | ||
if (!this._status && [...this._pending].every(path => this._waiting.get(path))) { | ||
// all pending files are currently waiting for one or more other files to exist | ||
// break deadlock: assume all files that have not appeared yet will never do so | ||
for (let path of this._available.keys()) if (!this._transforming.has(path)) this._found(path); | ||
for (let path of this._available.keys()) if (!this._pending.has(path)) this._found(path); | ||
} | ||
@@ -394,0 +414,0 @@ } |
{ | ||
"name": "defiler", | ||
"version": "0.11.0", | ||
"version": "0.11.1", | ||
"description": "A small, strange building block", | ||
@@ -5,0 +5,0 @@ "keywords": ["build", "framework", "async", "watch"], |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
92974
840