PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/metro-file-map/src/lib

Просмотр файла: FileProcessor.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 {
  FileMetadata,
  PerfLogger,
  WorkerMessage,
  WorkerMetadata,
  WorkerSetupArgs,
} from '../flow-types';

import H from '../constants';
import {Worker} from '../worker';
import {Worker as JestWorker} from 'jest-worker';
import {sep} from 'path';

// eslint-disable-next-line import/no-commonjs
const debug = require('debug')('Metro:FileMap');

type ProcessFileRequest = $ReadOnly<{
  /**
   * Populate metadata[H.SHA1] with the SHA1 of the file's contents.
   */
  computeSha1: boolean,
  /**
   * Populate metadata[H.DEPENDENCIES] with unresolved dependency specifiers
   * using the dependencyExtractor provided to the constructor.
   */
  computeDependencies: boolean,
  /**
   * Only if processing has already required reading the file's contents, return
   * the contents as a Buffer - null otherwise. Not supported for batches.
   */
  maybeReturnContent: boolean,
}>;

interface AsyncWorker {
  +processFile: WorkerMessage => Promise<WorkerMetadata>;
  +end: () => Promise<void>;
}

interface MaybeCodedError extends Error {
  code?: string;
}

const NODE_MODULES = sep + 'node_modules' + sep;
const MAX_FILES_PER_WORKER = 100;

export class FileProcessor {
  #dependencyExtractor: ?string;
  #enableHastePackages: boolean;
  #hasteImplModulePath: ?string;
  #enableWorkerThreads: boolean;
  #maxFilesPerWorker: number;
  #maxWorkers: number;
  #perfLogger: ?PerfLogger;
  #workerArgs: WorkerSetupArgs;
  #inBandWorker: Worker;

  constructor(
    opts: $ReadOnly<{
      dependencyExtractor: ?string,
      enableHastePackages: boolean,
      enableWorkerThreads: boolean,
      hasteImplModulePath: ?string,
      maxFilesPerWorker?: ?number,
      maxWorkers: number,
      perfLogger: ?PerfLogger,
    }>,
  ) {
    this.#dependencyExtractor = opts.dependencyExtractor;
    this.#enableHastePackages = opts.enableHastePackages;
    this.#enableWorkerThreads = opts.enableWorkerThreads;
    this.#hasteImplModulePath = opts.hasteImplModulePath;
    this.#maxFilesPerWorker = opts.maxFilesPerWorker ?? MAX_FILES_PER_WORKER;
    this.#maxWorkers = opts.maxWorkers;
    this.#workerArgs = {};
    this.#inBandWorker = new Worker(this.#workerArgs);
    this.#perfLogger = opts.perfLogger;
  }

  async processBatch(
    files: $ReadOnlyArray<[string /*absolutePath*/, FileMetadata]>,
    req: ProcessFileRequest,
  ): Promise<{
    errors: Array<{
      absolutePath: string,
      error: MaybeCodedError,
    }>,
  }> {
    const errors = [];
    const numWorkers = Math.min(
      this.#maxWorkers,
      Math.ceil(files.length / this.#maxFilesPerWorker),
    );
    const batchWorker = this.#getBatchWorker(numWorkers);

    if (req.maybeReturnContent) {
      throw new Error(
        'Batch processing does not support returning file contents',
      );
    }

    await Promise.all(
      files.map(([absolutePath, fileMetadata]) => {
        const maybeWorkerInput = this.#getWorkerInput(
          absolutePath,
          fileMetadata,
          req,
        );
        if (!maybeWorkerInput) {
          return null;
        }
        return batchWorker
          .processFile(maybeWorkerInput)
          .then(reply => processWorkerReply(reply, fileMetadata))
          .catch(error =>
            errors.push({absolutePath, error: normalizeWorkerError(error)}),
          );
      }),
    );
    await batchWorker.end();
    return {errors};
  }

  processRegularFile(
    absolutePath: string,
    fileMetadata: FileMetadata,
    req: ProcessFileRequest,
  ): ?{content: ?Buffer} {
    const workerInput = this.#getWorkerInput(absolutePath, fileMetadata, req);
    return workerInput
      ? {
          content: processWorkerReply(
            this.#inBandWorker.processFile(workerInput),
            fileMetadata,
          ),
        }
      : null;
  }

  #getWorkerInput(
    absolutePath: string,
    fileMetadata: FileMetadata,
    req: ProcessFileRequest,
  ): ?WorkerMessage {
    const computeSha1 = req.computeSha1 && fileMetadata[H.SHA1] == null;

    const {computeDependencies, maybeReturnContent} = req;

    // Use a cheaper worker configuration for node_modules files, because we
    // never care about extracting dependencies, and they may never be Haste
    // modules or packages.
    //
    // Note that we'd only expect node_modules files to reach this point if
    // retainAllFiles is true, or they're touched during watch mode.
    if (absolutePath.includes(NODE_MODULES)) {
      if (computeSha1) {
        return {
          computeDependencies: false,
          computeSha1: true,
          dependencyExtractor: null,
          enableHastePackages: false,
          filePath: absolutePath,
          hasteImplModulePath: null,
          maybeReturnContent,
        };
      }
      return null;
    }

    return {
      computeDependencies,
      computeSha1,
      dependencyExtractor: this.#dependencyExtractor,
      enableHastePackages: this.#enableHastePackages,
      filePath: absolutePath,
      hasteImplModulePath: this.#hasteImplModulePath,
      maybeReturnContent,
    };
  }

  /**
   * Creates workers or parses files and extracts metadata in-process.
   */
  #getBatchWorker(numWorkers: number): AsyncWorker {
    if (numWorkers <= 1) {
      // In-band worker with the same interface as a Jest worker farm
      return {
        processFile: async message => this.#inBandWorker.processFile(message),
        end: async () => {},
      };
    }
    const workerPath = require.resolve('../worker');
    debug(
      'Creating worker farm of %d worker %s',
      numWorkers,
      this.#enableWorkerThreads ? 'threads' : 'processes',
    );
    this.#perfLogger?.point('initWorkers_start');
    const jestWorker = new JestWorker<{
      processFile: WorkerMessage => Promise<WorkerMetadata>,
    }>(workerPath, {
      exposedMethods: ['processFile'],
      maxRetries: 3,
      numWorkers,
      enableWorkerThreads: this.#enableWorkerThreads,
      forkOptions: {
        // Don't pass Node arguments down to workers. In particular, avoid
        // unnecessarily registering Babel when we're running Metro from
        // source (our worker is plain CommonJS).
        execArgv: [],
      },
      setupArgs: [this.#workerArgs],
    });
    this.#perfLogger?.point('initWorkers_end');
    // Only log worker init once
    this.#perfLogger = null;
    return jestWorker;
  }

  async end(): Promise<void> {}
}

function processWorkerReply(
  metadata: WorkerMetadata,
  fileMetadata: FileMetadata,
) {
  fileMetadata[H.VISITED] = 1;

  const metadataId = metadata.id;

  if (metadataId != null) {
    fileMetadata[H.ID] = metadataId;
  }

  fileMetadata[H.DEPENDENCIES] = metadata.dependencies
    ? metadata.dependencies.join(H.DEPENDENCY_DELIM)
    : '';

  if (metadata.sha1 != null) {
    fileMetadata[H.SHA1] = metadata.sha1;
  }

  return metadata.content;
}

function normalizeWorkerError(mixedError: ?Error | string): MaybeCodedError {
  if (
    mixedError == null ||
    typeof mixedError !== 'object' ||
    mixedError.message == null ||
    mixedError.stack == null
  ) {
    const error = new Error(mixedError);
    error.stack = ''; // Remove stack for stack-less errors.
    return error;
  }
  return mixedError;
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!