PHP WebShell
Текущая директория: /usr/lib/node_modules/bitgo/node_modules/metro-file-map/src/crawlers/watchman
Просмотр файла: index.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 {WatchmanClockSpec} from '../../flow-types';
import type {
CanonicalPath,
CrawlerOptions,
FileData,
FileMetadata,
Path,
WatchmanClocks,
} from '../../flow-types';
import type {WatchmanQueryResponse, WatchmanWatchResponse} from 'fb-watchman';
import normalizePathSeparatorsToPosix from '../../lib/normalizePathSeparatorsToPosix';
import normalizePathSeparatorsToSystem from '../../lib/normalizePathSeparatorsToSystem';
import {RootPathUtils} from '../../lib/RootPathUtils';
import {planQuery} from './planQuery';
import watchman from 'fb-watchman';
import invariant from 'invariant';
import * as path from 'path';
import {performance} from 'perf_hooks';
type WatchmanRoots = Map<
string, // Posix-separated absolute path
$ReadOnly<{directoryFilters: Array<string>, watcher: string}>,
>;
const WATCHMAN_WARNING_INITIAL_DELAY_MILLISECONDS = 10000;
const WATCHMAN_WARNING_INTERVAL_MILLISECONDS = 20000;
const watchmanURL = 'https://facebook.github.io/watchman/docs/troubleshooting';
function makeWatchmanError(error: Error): Error {
error.message =
`Watchman error: ${error.message.trim()}. Make sure watchman ` +
`is running for this project. See ${watchmanURL}.`;
return error;
}
export default async function watchmanCrawl({
abortSignal,
computeSha1,
extensions,
ignore,
includeSymlinks,
onStatus,
perfLogger,
previousState,
rootDir,
roots,
}: CrawlerOptions): Promise<{
changedFiles: FileData,
removedFiles: Set<CanonicalPath>,
clocks: WatchmanClocks,
}> {
abortSignal?.throwIfAborted();
const client = new watchman.Client();
const pathUtils = new RootPathUtils(rootDir);
abortSignal?.addEventListener('abort', () => client.end());
perfLogger?.point('watchmanCrawl_start');
const newClocks = new Map<Path, WatchmanClockSpec>();
let clientError;
client.on('error', error => {
clientError = makeWatchmanError(error);
});
const cmd = async <T>(
command: 'watch-project' | 'query',
// $FlowFixMe[unclear-type] - Fix to use fb-watchman types
...args: Array<any>
): Promise<T> => {
let didLogWatchmanWaitMessage = false;
const startTime = performance.now();
const logWatchmanWaitMessage = () => {
didLogWatchmanWaitMessage = true;
onStatus({
type: 'watchman_slow_command',
timeElapsed: performance.now() - startTime,
command,
});
};
let intervalOrTimeoutId: TimeoutID | IntervalID = setTimeout(() => {
logWatchmanWaitMessage();
intervalOrTimeoutId = setInterval(
logWatchmanWaitMessage,
WATCHMAN_WARNING_INTERVAL_MILLISECONDS,
);
}, WATCHMAN_WARNING_INITIAL_DELAY_MILLISECONDS);
try {
const response = await new Promise<WatchmanQueryResponse>(
(resolve, reject) =>
// $FlowFixMe[incompatible-type] - dynamic call of command
client.command(
[command, ...args],
(error: ?Error, result: WatchmanQueryResponse) =>
error ? reject(makeWatchmanError(error)) : resolve(result),
),
);
if ('warning' in response) {
onStatus({
type: 'watchman_warning',
warning: response.warning,
command,
});
}
// $FlowFixMe[incompatible-type]
return response;
} finally {
// $FlowFixMe[incompatible-type] clearInterval / clearTimeout are interchangeable
clearInterval(intervalOrTimeoutId);
if (didLogWatchmanWaitMessage) {
onStatus({
type: 'watchman_slow_command_complete',
timeElapsed: performance.now() - startTime,
command,
});
}
}
};
async function getWatchmanRoots(
roots: $ReadOnlyArray<Path>,
): Promise<WatchmanRoots> {
perfLogger?.point('watchmanCrawl/getWatchmanRoots_start');
const watchmanRoots: WatchmanRoots = new Map();
await Promise.all(
roots.map(async (root, index) => {
perfLogger?.point(`watchmanCrawl/watchProject_${index}_start`);
const response = await cmd<WatchmanWatchResponse>(
'watch-project',
root,
);
perfLogger?.point(`watchmanCrawl/watchProject_${index}_end`);
const existing = watchmanRoots.get(response.watch);
// A root can only be filtered if it was never seen with a
// relative_path before.
const canBeFiltered = !existing || existing.directoryFilters.length > 0;
if (canBeFiltered) {
if (response.relative_path) {
watchmanRoots.set(response.watch, {
watcher: response.watcher,
directoryFilters: (existing?.directoryFilters || []).concat(
response.relative_path,
),
});
} else {
// Make the filter directories an empty array to signal that this
// root was already seen and needs to be watched for all files or
// directories.
watchmanRoots.set(response.watch, {
watcher: response.watcher,
directoryFilters: [],
});
}
}
}),
);
perfLogger?.point('watchmanCrawl/getWatchmanRoots_end');
return watchmanRoots;
}
async function queryWatchmanForDirs(rootProjectDirMappings: WatchmanRoots) {
perfLogger?.point('watchmanCrawl/queryWatchmanForDirs_start');
const results = new Map<string, WatchmanQueryResponse>();
let isFresh = false;
await Promise.all(
Array.from(rootProjectDirMappings).map(
async ([posixSeparatedRoot, {directoryFilters, watcher}], index) => {
// Jest is only going to store one type of clock; a string that
// represents a local clock. However, the Watchman crawler supports
// a second type of clock that can be written by automation outside of
// Jest, called an "scm query", which fetches changed files based on
// source control mergebases. The reason this is necessary is because
// local clocks are not portable across systems, but scm queries are.
// By using scm queries, we can create the haste map on a different
// system and import it, transforming the clock into a local clock.
const since = previousState.clocks.get(
normalizePathSeparatorsToPosix(
pathUtils.absoluteToNormal(
normalizePathSeparatorsToSystem(posixSeparatedRoot),
),
),
);
perfLogger?.annotate({
bool: {
[`watchmanCrawl/query_${index}_has_clock`]: since != null,
},
});
const {query, queryGenerator} = planQuery({
since,
extensions,
directoryFilters,
includeSha1: computeSha1,
includeSymlinks,
});
perfLogger?.annotate({
string: {
[`watchmanCrawl/query_${index}_watcher`]: watcher ?? 'unknown',
[`watchmanCrawl/query_${index}_generator`]: queryGenerator,
},
});
perfLogger?.point(`watchmanCrawl/query_${index}_start`);
const response = await cmd<WatchmanQueryResponse>(
'query',
posixSeparatedRoot,
query,
);
perfLogger?.point(`watchmanCrawl/query_${index}_end`);
// When a source-control query is used, we ignore the "is fresh"
// response from Watchman because it will be true despite the query
// being incremental.
const isSourceControlQuery =
typeof since !== 'string' && since?.scm?.['mergebase-with'] != null;
if (!isSourceControlQuery) {
isFresh = isFresh || response.is_fresh_instance;
}
results.set(posixSeparatedRoot, response);
},
),
);
perfLogger?.point('watchmanCrawl/queryWatchmanForDirs_end');
return {
isFresh,
results,
};
}
let removedFiles: Set<CanonicalPath> = new Set();
let changedFiles: FileData = new Map();
let results: Map<string, WatchmanQueryResponse>;
let isFresh = false;
let queryError: ?Error;
try {
const watchmanRoots = await getWatchmanRoots(roots);
const watchmanFileResults = await queryWatchmanForDirs(watchmanRoots);
results = watchmanFileResults.results;
isFresh = watchmanFileResults.isFresh;
} catch (e) {
queryError = e;
}
client.end();
if (results == null) {
if (clientError) {
perfLogger?.annotate({
string: {
'watchmanCrawl/client_error':
clientError.message ?? '[message missing]',
},
});
}
if (queryError) {
perfLogger?.annotate({
string: {
'watchmanCrawl/query_error':
queryError.message ?? '[message missing]',
},
});
}
perfLogger?.point('watchmanCrawl_end');
abortSignal?.throwIfAborted();
throw (
queryError ?? clientError ?? new Error('Watchman file results missing')
);
}
perfLogger?.point('watchmanCrawl/processResults_start');
const freshFileData: FileData = new Map();
for (const [watchRoot, response] of results) {
const fsRoot = normalizePathSeparatorsToSystem(watchRoot);
const relativeFsRoot = pathUtils.absoluteToNormal(fsRoot);
newClocks.set(
normalizePathSeparatorsToPosix(relativeFsRoot),
// Ensure we persist only the local clock.
typeof response.clock === 'string'
? response.clock
: response.clock.clock,
);
for (const fileData of response.files) {
const filePath =
fsRoot + path.sep + normalizePathSeparatorsToSystem(fileData.name);
const relativeFilePath = pathUtils.absoluteToNormal(filePath);
if (!fileData.exists) {
if (!isFresh) {
removedFiles.add(relativeFilePath);
}
// Whether watchman can return exists: false in a fresh instance
// response is unknown, but there's nothing we need to do in that case.
} else if (!ignore(filePath)) {
const {mtime_ms, size} = fileData;
invariant(
mtime_ms != null && size != null,
'missing file data in watchman response',
);
const mtime =
typeof mtime_ms === 'number' ? mtime_ms : mtime_ms.toNumber();
let sha1hex = fileData['content.sha1hex'];
if (typeof sha1hex !== 'string' || sha1hex.length !== 40) {
sha1hex = undefined;
}
let symlinkInfo: 0 | 1 | string = 0;
if (fileData.type === 'l') {
symlinkInfo = fileData['symlink_target'] ?? 1;
}
const nextData: FileMetadata = [
mtime,
size,
0,
'',
sha1hex ?? null,
symlinkInfo,
'',
];
// If watchman is fresh, the removed files map starts with all files
// and we remove them as we verify they still exist.
if (isFresh) {
freshFileData.set(relativeFilePath, nextData);
} else {
changedFiles.set(relativeFilePath, nextData);
}
}
}
}
if (isFresh) {
({changedFiles, removedFiles} =
previousState.fileSystem.getDifference(freshFileData));
}
perfLogger?.point('watchmanCrawl/processResults_end');
perfLogger?.point('watchmanCrawl_end');
abortSignal?.throwIfAborted();
return {
changedFiles,
removedFiles,
clocks: newClocks,
};
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!