| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e02dbf0 commit bbd1351
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1098,7 +1098,7 @@ async function* _watch(filename, options = kEmptyObject) { | |||
| 1098 | 1098 | // e.g. Linux due to the limitations of inotify. | |
| 1099 | 1099 | if (options.recursive && !isOSX && !isWindows) { | |
| 1100 | 1100 | const watcher = new nonNativeWatcher.FSWatcher(options); | |
| 1101 | - await watcher[kFSWatchStart](filename); | ||
| 1101 | + watcher[kFSWatchStart](filename); | ||
| 1102 | 1102 | yield* watcher; | |
| 1103 | 1103 | return; | |
| 1104 | 1104 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,7 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const { | |
| 4 | - ArrayPrototypePush, | ||
| 5 | - SafePromiseAllReturnVoid, | ||
| 6 | 4 | Promise, | |
| 7 | - PromisePrototypeThen, | ||
| 8 | 5 | SafeMap, | |
| 9 | 6 | SafeSet, | |
| 10 | 7 | StringPrototypeStartsWith, | |
@@ -31,47 +28,19 @@ const { | |||
| 31 | 28 | } = require('path'); | |
| 32 | 29 | ||
| 33 | 30 | let internalSync; | |
| 34 | - let internalPromises; | ||
| 35 | - | ||
| 36 | - function lazyLoadFsPromises() { | ||
| 37 | - internalPromises ??= require('fs/promises'); | ||
| 38 | - return internalPromises; | ||
| 39 | - } | ||
| 40 | 31 | ||
| 41 | 32 | function lazyLoadFsSync() { | |
| 42 | 33 | internalSync ??= require('fs'); | |
| 43 | 34 | return internalSync; | |
| 44 | 35 | } | |
| 45 | - let kResistStopPropagation; | ||
| 46 | - | ||
| 47 | - async function traverse(dir, files = new SafeMap(), symbolicLinks = new SafeSet()) { | ||
| 48 | - const { opendir } = lazyLoadFsPromises(); | ||
| 49 | - | ||
| 50 | - const filenames = await opendir(dir); | ||
| 51 | - const subdirectories = []; | ||
| 52 | - | ||
| 53 | - for await (const file of filenames) { | ||
| 54 | - const f = pathJoin(dir, file.name); | ||
| 55 | - | ||
| 56 | - files.set(f, file); | ||
| 57 | - | ||
| 58 | - // Do not follow symbolic links | ||
| 59 | - if (file.isSymbolicLink()) { | ||
| 60 | - symbolicLinks.add(f); | ||
| 61 | - } else if (file.isDirectory()) { | ||
| 62 | - ArrayPrototypePush(subdirectories, traverse(f, files)); | ||
| 63 | - } | ||
| 64 | - } | ||
| 65 | - | ||
| 66 | - await SafePromiseAllReturnVoid(subdirectories); | ||
| 67 | 36 | ||
| 68 | - return files; | ||
| 69 | - } | ||
| 37 | + let kResistStopPropagation; | ||
| 70 | 38 | ||
| 71 | 39 | class FSWatcher extends EventEmitter { | |
| 72 | 40 | #options = null; | |
| 73 | 41 | #closed = false; | |
| 74 | 42 | #files = new SafeMap(); | |
| 43 | + #watchers = new SafeMap(); | ||
| 75 | 44 | #symbolicFiles = new SafeSet(); | |
| 76 | 45 | #rootPath = pathResolve(); | |
| 77 | 46 | #watchingFile = false; | |
@@ -111,11 +80,11 @@ class FSWatcher extends EventEmitter { | |||
| 111 | 80 | return; | |
| 112 | 81 | } | |
| 113 | 82 | ||
| 114 | - const { unwatchFile } = lazyLoadFsSync(); | ||
| 115 | 83 | this.#closed = true; | |
| 116 | 84 | ||
| 117 | 85 | for (const file of this.#files.keys()) { | |
| 118 | - unwatchFile(file); | ||
| 86 | + this.#watchers.get(file).close(); | ||
| 87 | + this.#watchers.delete(file); | ||
| 119 | 88 | } | |
| 120 | 89 | ||
| 121 | 90 | this.#files.clear(); | |
@@ -124,24 +93,26 @@ class FSWatcher extends EventEmitter { | |||
| 124 | 93 | } | |
| 125 | 94 | ||
| 126 | 95 | #unwatchFiles(file) { | |
| 127 | - const { unwatchFile } = lazyLoadFsSync(); | ||
| 128 | - | ||
| 129 | 96 | this.#symbolicFiles.delete(file); | |
| 130 | 97 | ||
| 131 | 98 | for (const filename of this.#files.keys()) { | |
| 132 | 99 | if (StringPrototypeStartsWith(filename, file)) { | |
| 133 | - unwatchFile(filename); | ||
| 100 | + this.#files.delete(filename); | ||
| 101 | + this.#watchers.get(filename).close(); | ||
| 102 | + this.#watchers.delete(filename); | ||
| 134 | 103 | } | |
| 135 | 104 | } | |
| 136 | 105 | } | |
| 137 | 106 | ||
| 138 | - async #watchFolder(folder) { | ||
| 139 | - const { opendir } = lazyLoadFsPromises(); | ||
| 107 | + #watchFolder(folder) { | ||
| 108 | + const { readdirSync } = lazyLoadFsSync(); | ||
| 140 | 109 | ||
| 141 | 110 | try { | |
| 142 | - const files = await opendir(folder); | ||
| 111 | + const files = readdirSync(folder, { | ||
| 112 | + withFileTypes: true, | ||
| 113 | + }); | ||
| 143 | 114 | ||
| 144 | - for await (const file of files) { | ||
| 115 | + for (const file of files) { | ||
| 145 | 116 | if (this.#closed) { | |
| 146 | 117 | break; | |
| 147 | 118 | } | |
@@ -155,11 +126,9 @@ class FSWatcher extends EventEmitter { | |||
| 155 | 126 | this.#symbolicFiles.add(f); | |
| 156 | 127 | } | |
| 157 | 128 | ||
| 158 | - this.#files.set(f, file); | ||
| 159 | - if (file.isFile()) { | ||
| 160 | - this.#watchFile(f); | ||
| 161 | - } else if (file.isDirectory() && !file.isSymbolicLink()) { | ||
| 162 | - await this.#watchFolder(f); | ||
| 129 | + this.#watchFile(f); | ||
| 130 | + if (file.isDirectory() && !file.isSymbolicLink()) { | ||
| 131 | + this.#watchFolder(f); | ||
| 163 | 132 | } | |
| 164 | 133 | } | |
| 165 | 134 | } | |
@@ -173,22 +142,30 @@ class FSWatcher extends EventEmitter { | |||
| 173 | 142 | return; | |
| 174 | 143 | } | |
| 175 | 144 | ||
| 176 | - const { watchFile } = lazyLoadFsSync(); | ||
| 177 | - const existingStat = this.#files.get(file); | ||
| 145 | + const { watch, statSync } = lazyLoadFsSync(); | ||
| 146 | + | ||
| 147 | + if (this.#files.has(file)) { | ||
| 148 | + return; | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + { | ||
| 152 | + const existingStat = statSync(file); | ||
| 153 | + this.#files.set(file, existingStat); | ||
| 154 | + } | ||
| 178 | 155 | ||
| 179 | - watchFile(file, { | ||
| 156 | + const watcher = watch(file, { | ||
| 180 | 157 | persistent: this.#options.persistent, | |
| 181 | - }, (currentStats, previousStats) => { | ||
| 182 | - if (existingStat && !existingStat.isDirectory() && | ||
| 183 | - currentStats.nlink !== 0 && existingStat.mtimeMs === currentStats.mtimeMs) { | ||
| 184 | - return; | ||
| 185 | - } | ||
| 158 | + }, (eventType, filename) => { | ||
| 159 | + const existingStat = this.#files.get(file); | ||
| 160 | + const currentStats = statSync(file); | ||
| 186 | 161 | ||
| 187 | 162 | this.#files.set(file, currentStats); | |
| 188 | 163 | ||
| 189 | - if (currentStats.birthtimeMs === 0 && previousStats.birthtimeMs !== 0) { | ||
| 164 | + if (currentStats.birthtimeMs === 0 && existingStat.birthtimeMs !== 0) { | ||
| 190 | 165 | // The file is now deleted | |
| 191 | 166 | this.#files.delete(file); | |
| 167 | + this.#watchers.delete(file); | ||
| 168 | + watcher.close(); | ||
| 192 | 169 | this.emit('change', 'rename', pathRelative(this.#rootPath, file)); | |
| 193 | 170 | this.#unwatchFiles(file); | |
| 194 | 171 | } else if (file === this.#rootPath && this.#watchingFile) { | |
@@ -205,6 +182,7 @@ class FSWatcher extends EventEmitter { | |||
| 205 | 182 | this.emit('change', 'change', pathRelative(this.#rootPath, file)); | |
| 206 | 183 | } | |
| 207 | 184 | }); | |
| 185 | + this.#watchers.set(file, watcher); | ||
| 208 | 186 | } | |
| 209 | 187 | ||
| 210 | 188 | [kFSWatchStart](filename) { | |
@@ -217,19 +195,9 @@ class FSWatcher extends EventEmitter { | |||
| 217 | 195 | this.#closed = false; | |
| 218 | 196 | this.#watchingFile = file.isFile(); | |
| 219 | 197 | ||
| 198 | + this.#watchFile(filename); | ||
| 220 | 199 | if (file.isDirectory()) { | |
| 221 | - this.#files.set(filename, file); | ||
| 222 | - | ||
| 223 | - PromisePrototypeThen( | ||
| 224 | - traverse(filename, this.#files, this.#symbolicFiles), | ||
| 225 | - () => { | ||
| 226 | - for (const f of this.#files.keys()) { | ||
| 227 | - this.#watchFile(f); | ||
| 228 | - } | ||
| 229 | - }, | ||
| 230 | - ); | ||
| 231 | - } else { | ||
| 232 | - this.#watchFile(filename); | ||
| 200 | + this.#watchFolder(filename); | ||
| 233 | 201 | } | |
| 234 | 202 | } catch (error) { | |
| 235 | 203 | if (error.code === 'ENOENT') { | |
@@ -264,7 +232,10 @@ class FSWatcher extends EventEmitter { | |||
| 264 | 232 | resolve({ __proto__: null, value: { eventType, filename } }); | |
| 265 | 233 | }); | |
| 266 | 234 | } : (resolve, reject) => { | |
| 267 | - const onAbort = () => reject(new AbortError(undefined, { cause: signal.reason })); | ||
| 235 | + const onAbort = () => { | ||
| 236 | + this.close(); | ||
| 237 | + reject(new AbortError(undefined, { cause: signal.reason })); | ||
| 238 | + }; | ||
| 268 | 239 | if (signal.aborted) return onAbort(); | |
| 269 | 240 | kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation; | |
| 270 | 241 | signal.addEventListener('abort', onAbort, { __proto__: null, once: true, [kResistStopPropagation]: true }); | |
@@ -277,6 +248,10 @@ class FSWatcher extends EventEmitter { | |||
| 277 | 248 | next: () => (this.#closed ? | |
| 278 | 249 | { __proto__: null, done: true } : | |
| 279 | 250 | new Promise(promiseExecutor)), | |
| 251 | + return: () => { | ||
| 252 | + this.close(); | ||
| 253 | + return { __proto__: null, done: true }; | ||
| 254 | + }, | ||
| 280 | 255 | [SymbolAsyncIterator]() { return this; }, | |
| 281 | 256 | }; | |
| 282 | 257 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,6 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const common = require('../common'); | |
| 4 | - const { setTimeout } = require('timers/promises'); | ||
| 5 | 4 | ||
| 6 | 5 | if (common.isIBMi) | |
| 7 | 6 | common.skip('IBMi does not support `fs.watch()`'); | |
@@ -21,39 +20,36 @@ const tmpdir = require('../common/tmpdir'); | |||
| 21 | 20 | const testDir = tmpdir.path; | |
| 22 | 21 | tmpdir.refresh(); | |
| 23 | 22 | ||
| 24 | - (async () => { | ||
| 25 | - // Add a file to subfolder of a watching folder | ||
| 23 | + // Add a file to subfolder of a watching folder | ||
| 26 | 24 | ||
| 27 | - const rootDirectory = fs.mkdtempSync(testDir + path.sep); | ||
| 28 | - const testDirectory = path.join(rootDirectory, 'test-4'); | ||
| 29 | - fs.mkdirSync(testDirectory); | ||
| 25 | + const rootDirectory = fs.mkdtempSync(testDir + path.sep); | ||
| 26 | + const testDirectory = path.join(rootDirectory, 'test-4'); | ||
| 27 | + fs.mkdirSync(testDirectory); | ||
| 30 | 28 | ||
| 31 | - const file = 'folder-5'; | ||
| 32 | - const filePath = path.join(testDirectory, file); | ||
| 33 | - fs.mkdirSync(filePath); | ||
| 29 | + const file = 'folder-5'; | ||
| 30 | + const filePath = path.join(testDirectory, file); | ||
| 31 | + fs.mkdirSync(filePath); | ||
| 34 | 32 | ||
| 35 | - const subfolderPath = path.join(filePath, 'subfolder-6'); | ||
| 36 | - fs.mkdirSync(subfolderPath); | ||
| 33 | + const subfolderPath = path.join(filePath, 'subfolder-6'); | ||
| 34 | + fs.mkdirSync(subfolderPath); | ||
| 37 | 35 | ||
| 38 | - const childrenFile = 'file-7.txt'; | ||
| 39 | - const childrenAbsolutePath = path.join(subfolderPath, childrenFile); | ||
| 40 | - const relativePath = path.join(file, path.basename(subfolderPath), childrenFile); | ||
| 36 | + const childrenFile = 'file-7.txt'; | ||
| 37 | + const childrenAbsolutePath = path.join(subfolderPath, childrenFile); | ||
| 38 | + const relativePath = path.join(file, path.basename(subfolderPath), childrenFile); | ||
| 41 | 39 | ||
| 42 | - const watcher = fs.watch(testDirectory, { recursive: true }); | ||
| 43 | - let watcherClosed = false; | ||
| 44 | - watcher.on('change', function(event, filename) { | ||
| 45 | - assert.strictEqual(event, 'rename'); | ||
| 40 | + const watcher = fs.watch(testDirectory, { recursive: true }); | ||
| 41 | + let watcherClosed = false; | ||
| 42 | + watcher.on('change', function(event, filename) { | ||
| 43 | + assert.strictEqual(event, 'rename'); | ||
| 46 | 44 | ||
| 47 | - if (filename === relativePath) { | ||
| 48 | - watcher.close(); | ||
| 49 | - watcherClosed = true; | ||
| 50 | - } | ||
| 51 | - }); | ||
| 45 | + if (filename === relativePath) { | ||
| 46 | + watcher.close(); | ||
| 47 | + watcherClosed = true; | ||
| 48 | + } | ||
| 49 | + }); | ||
| 52 | 50 | ||
| 53 | - await setTimeout(common.platformTimeout(100)); | ||
| 54 | - fs.writeFileSync(childrenAbsolutePath, 'world'); | ||
| 51 | + fs.writeFileSync(childrenAbsolutePath, 'world'); | ||
| 55 | 52 | ||
| 56 | - process.once('exit', function() { | ||
| 57 | - assert(watcherClosed, 'watcher Object was not closed'); | ||
| 58 | - }); | ||
| 59 | - })().then(common.mustCall()); | ||
| 53 | + process.once('exit', function() { | ||
| 54 | + assert(watcherClosed, 'watcher Object was not closed'); | ||
| 55 | + }); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,6 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const common = require('../common'); | |
| 4 | - const { setTimeout } = require('timers/promises'); | ||
| 5 | 4 | ||
| 6 | 5 | if (common.isIBMi) | |
| 7 | 6 | common.skip('IBMi does not support `fs.watch()`'); | |
@@ -21,37 +20,33 @@ const tmpdir = require('../common/tmpdir'); | |||
| 21 | 20 | const testDir = tmpdir.path; | |
| 22 | 21 | tmpdir.refresh(); | |
| 23 | 22 | ||
| 24 | - (async () => { | ||
| 25 | - // Add a file to newly created folder to already watching folder | ||
| 23 | + // Add a file to newly created folder to already watching folder | ||
| 26 | 24 | ||
| 27 | - const rootDirectory = fs.mkdtempSync(testDir + path.sep); | ||
| 28 | - const testDirectory = path.join(rootDirectory, 'test-3'); | ||
| 29 | - fs.mkdirSync(testDirectory); | ||
| 25 | + const rootDirectory = fs.mkdtempSync(testDir + path.sep); | ||
| 26 | + const testDirectory = path.join(rootDirectory, 'test-3'); | ||
| 27 | + fs.mkdirSync(testDirectory); | ||
| 30 | 28 | ||
| 31 | - const filePath = path.join(testDirectory, 'folder-3'); | ||
| 29 | + const filePath = path.join(testDirectory, 'folder-3'); | ||
| 32 | 30 | ||
| 33 | - const childrenFile = 'file-4.txt'; | ||
| 34 | - const childrenAbsolutePath = path.join(filePath, childrenFile); | ||
| 35 | - const childrenRelativePath = path.join(path.basename(filePath), childrenFile); | ||
| 31 | + const childrenFile = 'file-4.txt'; | ||
| 32 | + const childrenAbsolutePath = path.join(filePath, childrenFile); | ||
| 33 | + const childrenRelativePath = path.join(path.basename(filePath), childrenFile); | ||
| 36 | 34 | ||
| 37 | - const watcher = fs.watch(testDirectory, { recursive: true }); | ||
| 38 | - let watcherClosed = false; | ||
| 39 | - watcher.on('change', function(event, filename) { | ||
| 40 | - assert.strictEqual(event, 'rename'); | ||
| 41 | - assert.ok(filename === path.basename(filePath) || filename === childrenRelativePath); | ||
| 35 | + const watcher = fs.watch(testDirectory, { recursive: true }); | ||
| 36 | + let watcherClosed = false; | ||
| 37 | + watcher.on('change', function(event, filename) { | ||
| 38 | + assert.strictEqual(event, 'rename'); | ||
| 39 | + assert.ok(filename === path.basename(filePath) || filename === childrenRelativePath); | ||
| 42 | 40 | ||
| 43 | - if (filename === childrenRelativePath) { | ||
| 44 | - watcher.close(); | ||
| 45 | - watcherClosed = true; | ||
| 46 | - } | ||
| 47 | - }); | ||
| 41 | + if (filename === childrenRelativePath) { | ||
| 42 | + watcher.close(); | ||
| 43 | + watcherClosed = true; | ||
| 44 | + } | ||
| 45 | + }); | ||
| 48 | 46 | ||
| 49 | - await setTimeout(common.platformTimeout(100)); | ||
| 50 | - fs.mkdirSync(filePath); | ||
| 51 | - await setTimeout(common.platformTimeout(100)); | ||
| 52 | - fs.writeFileSync(childrenAbsolutePath, 'world'); | ||
| 47 | + fs.mkdirSync(filePath); | ||
| 48 | + fs.writeFileSync(childrenAbsolutePath, 'world'); | ||
| 53 | 49 | ||
| 54 | - process.once('exit', function() { | ||
| 55 | - assert(watcherClosed, 'watcher Object was not closed'); | ||
| 56 | - }); | ||
| 57 | - })().then(common.mustCall()); | ||
| 50 | + process.once('exit', function() { | ||
| 51 | + assert(watcherClosed, 'watcher Object was not closed'); | ||
| 52 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments