BLUE
PHP 7.4.33
Path:
/var/www/uibuilder.cmshelp.dk/httpdocs/node_modules/metro-file-map/src/watchers
Run
Logout
Edit File
Size: 11.65 KB
Close
/var/www/uibuilder.cmshelp.dk/httpdocs/node_modules/metro-file-map/src/watchers/FallbackWatcher.js.flow
Text
Base64
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict-local * @format * @oncall react_native */ /** * Originally vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/node_watcher.js */ 'use strict'; import type { ChangeEventMetadata, WatcherBackendChangeEvent, } from '../flow-types'; import type {FSWatcher, Stats} from 'fs'; const {AbstractWatcher} = require('./AbstractWatcher'); const common = require('./common'); const fs = require('fs'); const platform = require('os').platform(); const path = require('path'); // $FlowFixMe[untyped-import] - Write libdefs for `walker` const walker = require('walker'); const fsPromises = fs.promises; const TOUCH_EVENT = common.TOUCH_EVENT; const DELETE_EVENT = common.DELETE_EVENT; /** * This setting delays all events. It suppresses 'change' events that * immediately follow an 'add', and debounces successive 'change' events to * only emit the latest. */ const DEBOUNCE_MS = 100; module.exports = class FallbackWatcher extends AbstractWatcher { +_changeTimers: Map<string, TimeoutID> = new Map(); +_dirRegistry: { [directory: string]: {[file: string]: true, __proto__: null}, __proto__: null, } = Object.create(null); +watched: {[key: string]: FSWatcher, __proto__: null} = Object.create(null); async startWatching() { this._watchdir(this.root); await new Promise(resolve => { recReaddir( this.root, dir => { this._watchdir(dir); }, filename => { this._register(filename, 'f'); }, symlink => { this._register(symlink, 'l'); }, () => { resolve(); }, this._checkedEmitError, this.ignored, ); }); } /** * Register files that matches our globs to know what to type of event to * emit in the future. * * Registry looks like the following: * * dirRegister => Map { * dirpath => Map { * filename => true * } * } * * Return false if ignored or already registered. */ _register(filepath: string, type: ChangeEventMetadata['type']): boolean { const dir = path.dirname(filepath); const filename = path.basename(filepath); if (this._dirRegistry[dir] && this._dirRegistry[dir][filename]) { return false; } const relativePath = path.relative(this.root, filepath); if ( this.doIgnore(relativePath) || (type === 'f' && !common.includedByGlob('f', this.globs, this.dot, relativePath)) ) { return false; } if (!this._dirRegistry[dir]) { this._dirRegistry[dir] = Object.create(null); } this._dirRegistry[dir][filename] = true; return true; } /** * Removes a file from the registry. */ _unregister(filepath: string) { const dir = path.dirname(filepath); if (this._dirRegistry[dir]) { const filename = path.basename(filepath); delete this._dirRegistry[dir][filename]; } } /** * Removes a dir from the registry. */ _unregisterDir(dirpath: string): void { if (this._dirRegistry[dirpath]) { delete this._dirRegistry[dirpath]; } } /** * Checks if a file or directory exists in the registry. */ _registered(fullpath: string): boolean { const dir = path.dirname(fullpath); return !!( this._dirRegistry[fullpath] || (this._dirRegistry[dir] && this._dirRegistry[dir][path.basename(fullpath)]) ); } /** * Emit "error" event if it's not an ignorable event */ _checkedEmitError: (error: Error) => void = error => { if (!isIgnorableFileError(error)) { this.emitError(error); } }; /** * Watch a directory. */ _watchdir: string => boolean = (dir: string) => { if (this.watched[dir]) { return false; } const watcher = fs.watch(dir, {persistent: true}, (event, filename) => this._normalizeChange(dir, event, filename), ); this.watched[dir] = watcher; watcher.on('error', this._checkedEmitError); if (this.root !== dir) { this._register(dir, 'd'); } return true; }; /** * Stop watching a directory. */ async _stopWatching(dir: string): Promise<void> { if (this.watched[dir]) { await new Promise(resolve => { this.watched[dir].once('close', () => process.nextTick(resolve)); this.watched[dir].close(); delete this.watched[dir]; }); } } /** * End watching. */ async stopWatching(): Promise<void> { await super.stopWatching(); const promises = Object.keys(this.watched).map(dir => this._stopWatching(dir), ); await Promise.all(promises); } /** * On some platforms, as pointed out on the fs docs (most likely just win32) * the file argument might be missing from the fs event. Try to detect what * change by detecting if something was deleted or the most recent file change. */ _detectChangedFile( dir: string, event: string, callback: (file: string) => void, ) { if (!this._dirRegistry[dir]) { return; } let found = false; let closest: ?$ReadOnly<{file: string, mtime: Stats['mtime']}> = null; let c = 0; Object.keys(this._dirRegistry[dir]).forEach((file, i, arr) => { fs.lstat(path.join(dir, file), (error, stat) => { if (found) { return; } if (error) { if (isIgnorableFileError(error)) { found = true; callback(file); } else { this.emitError(error); } } else { if (closest == null || stat.mtime > closest.mtime) { closest = {file, mtime: stat.mtime}; } if (arr.length === ++c) { callback(closest.file); } } }); }); } /** * Normalize fs events and pass it on to be processed. */ _normalizeChange(dir: string, event: string, file: string) { if (!file) { this._detectChangedFile(dir, event, actualFile => { if (actualFile) { this._processChange(dir, event, actualFile).catch(error => this.emitError(error), ); } }); } else { this._processChange(dir, event, path.normalize(file)).catch(error => this.emitError(error), ); } } /** * Process changes. */ async _processChange(dir: string, event: string, file: string) { const fullPath = path.join(dir, file); const relativePath = path.join(path.relative(this.root, dir), file); const registered = this._registered(fullPath); try { const stat = await fsPromises.lstat(fullPath); if (stat.isDirectory()) { // win32 emits usless change events on dirs. if (event === 'change') { return; } if ( this.doIgnore(relativePath) || !common.includedByGlob('d', this.globs, this.dot, relativePath) ) { return; } recReaddir( path.resolve(this.root, relativePath), (dir, stats) => { if (this._watchdir(dir)) { this._emitEvent({ event: TOUCH_EVENT, relativePath: path.relative(this.root, dir), metadata: { modifiedTime: stats.mtime.getTime(), size: stats.size, type: 'd', }, }); } }, (file, stats) => { if (this._register(file, 'f')) { this._emitEvent({ event: TOUCH_EVENT, relativePath: path.relative(this.root, file), metadata: { modifiedTime: stats.mtime.getTime(), size: stats.size, type: 'f', }, }); } }, (symlink, stats) => { if (this._register(symlink, 'l')) { this.emitFileEvent({ event: TOUCH_EVENT, relativePath: path.relative(this.root, symlink), metadata: { modifiedTime: stats.mtime.getTime(), size: stats.size, type: 'l', }, }); } }, function endCallback() {}, this._checkedEmitError, this.ignored, ); } else { const type = common.typeFromStat(stat); if (type == null) { return; } const metadata = { modifiedTime: stat.mtime.getTime(), size: stat.size, type, }; if (registered) { this._emitEvent({event: TOUCH_EVENT, relativePath, metadata}); } else { if (this._register(fullPath, type)) { this._emitEvent({event: TOUCH_EVENT, relativePath, metadata}); } } } } catch (error) { if (!isIgnorableFileError(error)) { this.emitError(error); return; } this._unregister(fullPath); this._unregisterDir(fullPath); if (registered) { this._emitEvent({event: DELETE_EVENT, relativePath}); } await this._stopWatching(fullPath); } } /** * Emits the given event after debouncing, to emit only the latest * information when we receive several events in quick succession. E.g., * Linux emits two events for every new file. * * See also note above for DEBOUNCE_MS. */ _emitEvent(change: Omit<WatcherBackendChangeEvent, 'root'>) { const {event, relativePath} = change; const key = event + '-' + relativePath; const existingTimer = this._changeTimers.get(key); if (existingTimer) { clearTimeout(existingTimer); } this._changeTimers.set( key, setTimeout(() => { this._changeTimers.delete(key); this.emitFileEvent(change); }, DEBOUNCE_MS), ); } getPauseReason(): ?string { return null; } }; /** * Determine if a given FS error can be ignored */ function isIgnorableFileError(error: Error | {code: string}) { return ( error.code === 'ENOENT' || // Workaround Windows EPERM on watched folder deletion, and when // reading locked files (pending further writes or pending deletion). // In such cases, we'll receive a subsequent event when the file is // deleted or ready to read. // https://github.com/facebook/metro/issues/1001 // https://github.com/nodejs/node-v0.x-archive/issues/4337 (error.code === 'EPERM' && platform === 'win32') ); } /** * Traverse a directory recursively calling `callback` on every directory. */ function recReaddir( dir: string, dirCallback: (string, Stats) => void, fileCallback: (string, Stats) => void, symlinkCallback: (string, Stats) => void, endCallback: () => void, errorCallback: Error => void, ignored: ?RegExp, ) { const walk = walker(dir); if (ignored) { walk.filterDir( (currentDir: string) => !common.posixPathMatchesPattern(ignored, currentDir), ); } walk .on('dir', normalizeProxy(dirCallback)) .on('file', normalizeProxy(fileCallback)) .on('symlink', normalizeProxy(symlinkCallback)) .on('error', errorCallback) .on('end', () => { if (platform === 'win32') { setTimeout(endCallback, 1000); } else { endCallback(); } }); } /** * Returns a callback that when called will normalize a path and call the * original callback */ function normalizeProxy<T>( callback: (filepath: string, stats: Stats) => T, ): (string, Stats) => T { return (filepath: string, stats: Stats) => callback(path.normalize(filepath), stats); }
Save
Close
Exit & Reset
Text mode: syntax highlighting auto-detects file type.
Directory Contents
Dirs: 0 × Files: 12
Delete Selected
Select All
Select None
Sort:
Name
Size
Modified
Enable drag-to-move
Name
Size
Perms
Modified
Actions
AbstractWatcher.js
2.17 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
AbstractWatcher.js.flow
2.04 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
common.js
1.11 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
common.js.flow
2.38 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
FallbackWatcher.js
8.43 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
FallbackWatcher.js.flow
11.65 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
NativeWatcher.js
3.02 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
NativeWatcher.js.flow
3.77 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
RecrawlWarning.js
1.16 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
RecrawlWarning.js.flow
1.73 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
WatchmanWatcher.js
8.73 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
WatchmanWatcher.js.flow
9.21 KB
lrw-r--r--
2025-03-28 11:04:32
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Zip Selected
If ZipArchive is unavailable, a
.tar
will be created (no compression).