PHP WebShell
Текущая директория: /usr/lib/node_modules/bitgo/node_modules/metro-file-map/src/watchers
Просмотр файла: WatchmanWatcher.js.flow
/**
* 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
*/
import type {WatcherOptions} from './common';
import type {
Client,
WatchmanClockResponse,
WatchmanFileChange,
WatchmanQuery,
WatchmanSubscribeResponse,
WatchmanSubscriptionEvent,
WatchmanWatchResponse,
} from 'fb-watchman';
import normalizePathSeparatorsToSystem from '../lib/normalizePathSeparatorsToSystem';
import {AbstractWatcher} from './AbstractWatcher';
import * as common from './common';
import RecrawlWarning from './RecrawlWarning';
import assert from 'assert';
import {createHash} from 'crypto';
import watchman from 'fb-watchman';
import invariant from 'invariant';
// eslint-disable-next-line import/no-commonjs
const debug = require('debug')('Metro:WatchmanWatcher');
const DELETE_EVENT = common.DELETE_EVENT;
const TOUCH_EVENT = common.TOUCH_EVENT;
const SUB_PREFIX = 'metro-file-map';
/**
* Watches `dir`.
*/
export default class WatchmanWatcher extends AbstractWatcher {
client: Client;
+subscriptionName: string;
watchProjectInfo: ?$ReadOnly<{
relativePath: string,
root: string,
}>;
+watchmanDeferStates: $ReadOnlyArray<string>;
#deferringStates: ?Set<string> = null;
constructor(dir: string, {watchmanDeferStates, ...opts}: WatcherOptions) {
super(dir, opts);
this.watchmanDeferStates = watchmanDeferStates;
// Use a unique subscription name per process per watched directory
const watchKey = createHash('md5').update(this.root).digest('hex');
const readablePath = this.root
.replace(/[\/\\]/g, '-') // \ and / to -
.replace(/[^\-\w]/g, ''); // Remove non-word/hyphen
this.subscriptionName = `${SUB_PREFIX}-${process.pid}-${readablePath}-${watchKey}`;
}
async startWatching() {
await new Promise((resolve, reject) => this._init(resolve, reject));
}
/**
* Run the watchman `watch` command on the root and subscribe to changes.
*/
_init(onReady: () => void, onError: (error: Error) => void) {
if (this.client) {
this.client.removeAllListeners();
}
const self = this;
this.client = new watchman.Client();
this.client.on('error', error => {
this.emitError(error);
});
this.client.on('subscription', changeEvent =>
this._handleChangeEvent(changeEvent),
);
this.client.on('end', () => {
console.warn(
'[metro-file-map] Warning: Lost connection to Watchman, reconnecting..',
);
self._init(
() => {},
error => self.emitError(error),
);
});
this.watchProjectInfo = null;
function getWatchRoot() {
return self.watchProjectInfo ? self.watchProjectInfo.root : self.root;
}
function onWatchProject(error: ?Error, resp: WatchmanWatchResponse) {
if (error) {
onError(error);
return;
}
debug('Received watch-project response: %s', resp.relative_path);
handleWarning(resp);
// NB: Watchman outputs posix-separated paths even on Windows, convert
// them to system-native separators.
self.watchProjectInfo = {
relativePath: resp.relative_path
? normalizePathSeparatorsToSystem(resp.relative_path)
: '',
root: normalizePathSeparatorsToSystem(resp.watch),
};
self.client.command(['clock', getWatchRoot()], onClock);
}
function onClock(error: ?Error, resp: WatchmanClockResponse) {
if (error) {
onError(error);
return;
}
debug('Received clock response: %s', resp.clock);
const watchProjectInfo = self.watchProjectInfo;
invariant(
watchProjectInfo != null,
'watch-project response should have been set before clock response',
);
handleWarning(resp);
const options: WatchmanQuery = {
fields: ['name', 'exists', 'new', 'type', 'size', 'mtime_ms'],
since: resp.clock,
defer: self.watchmanDeferStates,
relative_root: watchProjectInfo.relativePath,
};
// Make sure we honor the dot option if even we're not using globs.
if (self.globs.length === 0 && !self.dot) {
options.expression = [
'match',
'**',
'wholename',
{
includedotfiles: false,
},
];
}
self.client.command(
['subscribe', getWatchRoot(), self.subscriptionName, options],
onSubscribe,
);
}
const onSubscribe = (error: ?Error, resp: WatchmanSubscribeResponse) => {
if (error) {
onError(error);
return;
}
debug('Received subscribe response: %s', resp.subscribe);
handleWarning(resp);
if (resp['asserted-states'] != null) {
this.#deferringStates = new Set(resp['asserted-states']);
}
onReady();
};
self.client.command(['watch-project', getWatchRoot()], onWatchProject);
}
/**
* Handles a change event coming from the subscription.
*/
_handleChangeEvent(resp: WatchmanSubscriptionEvent) {
debug(
'Received subscription response: %s (fresh: %s, files: %s, enter: %s, leave: %s, clock: %s)',
resp.subscription,
resp.is_fresh_instance,
resp.files?.length,
resp['state-enter'],
resp['state-leave'],
resp.clock,
);
assert.equal(
resp.subscription,
this.subscriptionName,
'Invalid subscription event.',
);
if (Array.isArray(resp.files)) {
resp.files.forEach(change => this._handleFileChange(change, resp.clock));
}
const {'state-enter': stateEnter, 'state-leave': stateLeave} = resp;
if (
stateEnter != null &&
(this.watchmanDeferStates ?? []).includes(stateEnter)
) {
this.#deferringStates?.add(stateEnter);
debug(
'Watchman reports "%s" just started. Filesystem notifications are paused.',
stateEnter,
);
}
if (
stateLeave != null &&
(this.watchmanDeferStates ?? []).includes(stateLeave)
) {
this.#deferringStates?.delete(stateLeave);
debug(
'Watchman reports "%s" ended. Filesystem notifications resumed.',
stateLeave,
);
}
}
/**
* Handles a single change event record.
*/
_handleFileChange(
changeDescriptor: WatchmanFileChange,
rawClock: WatchmanSubscriptionEvent['clock'],
) {
const self = this;
const watchProjectInfo = self.watchProjectInfo;
invariant(
watchProjectInfo != null,
'watch-project response should have been set before receiving subscription events',
);
const {
name: relativePosixPath,
new: isNew = false,
exists = false,
type,
mtime_ms,
size,
} = changeDescriptor;
// Watchman emits posix-separated paths on Windows, which is inconsistent
// with other watchers. Normalize to system-native separators.
const relativePath = normalizePathSeparatorsToSystem(relativePosixPath);
debug(
'Handling change to: %s (new: %s, exists: %s, type: %s)',
relativePath,
isNew,
exists,
type,
);
// Ignore files of an unrecognized type
if (type != null && !(type === 'f' || type === 'd' || type === 'l')) {
return;
}
if (
this.doIgnore(relativePath) ||
!common.includedByGlob(type, this.globs, this.dot, relativePath)
) {
return;
}
const clock =
typeof rawClock === 'string' && this.watchProjectInfo != null
? ([this.watchProjectInfo.root, rawClock]: [string, string])
: undefined;
if (!exists) {
self.emitFileEvent({event: DELETE_EVENT, clock, relativePath});
} else {
invariant(
type != null && mtime_ms != null && size != null,
'Watchman file change event for "%s" missing some requested metadata. ' +
'Got type: %s, mtime_ms: %s, size: %s',
relativePath,
type,
mtime_ms,
size,
);
if (
// Change event on dirs are mostly useless.
!(type === 'd' && !isNew)
) {
const mtime = Number(mtime_ms);
self.emitFileEvent({
event: TOUCH_EVENT,
clock,
relativePath,
metadata: {
modifiedTime: mtime !== 0 ? mtime : null,
size,
type,
},
});
}
}
}
/**
* Closes the watcher.
*/
async stopWatching() {
await super.stopWatching();
if (this.client) {
this.client.removeAllListeners();
this.client.end();
}
this.#deferringStates = null;
}
getPauseReason(): ?string {
if (this.#deferringStates == null || this.#deferringStates.size === 0) {
return null;
}
const states = [...this.#deferringStates];
if (states.length === 1) {
return `The watch is in the '${states[0]}' state.`;
}
return `The watch is in the ${states
.slice(0, -1)
.map(s => `'${s}'`)
.join(', ')} and '${states[states.length - 1]}' states.`;
}
}
/**
* Handles a warning in the watchman resp object.
*/
function handleWarning(resp: $ReadOnly<{warning?: mixed, ...}>) {
if ('warning' in resp) {
if (RecrawlWarning.isRecrawlWarningDupe(resp.warning)) {
return true;
}
console.warn(resp.warning);
return true;
} else {
return false;
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!