static async glob(
    globby,
    compiler,
    compilation,
    logger,
    cache,
    concurrency,
    inputPattern,
    index,
  ) {
    const { RawSource } = compiler.webpack.sources;
    const pattern = { ...inputPattern };
    const originalFrom = pattern.from;
    const normalizedOriginalFrom = path.normalize(originalFrom);

    logger.log(
      `starting to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context`,
    );

    let absoluteFrom;

    if (path.isAbsolute(normalizedOriginalFrom)) {
      absoluteFrom = normalizedOriginalFrom;
    } else {
      absoluteFrom = path.resolve(pattern.context, normalizedOriginalFrom);
    }

    logger.debug(`getting stats for '${absoluteFrom}'...`);

    const { inputFileSystem } = compiler;

    let stats;

    try {
      // @ts-ignore
      stats = await stat(inputFileSystem, absoluteFrom);
    } catch (error) {
      // Nothing
    }

    /**
     * @type {"file" | "dir" | "glob"}
     */
    let fromType;

    if (stats) {
      if (stats.isDirectory()) {
        fromType = "dir";
        logger.debug(`determined '${absoluteFrom}' is a directory`);
      } else if (stats.isFile()) {
        fromType = "file";
        logger.debug(`determined '${absoluteFrom}' is a file`);
      } else {
        // Fallback
        fromType = "glob";
        logger.debug(`determined '${absoluteFrom}' is unknown`);
      }
    } else {
      fromType = "glob";
      logger.debug(`determined '${absoluteFrom}' is a glob`);
    }

    /** @type {GlobbyOptions} */
    const globOptions = {
      absolute: true,
      followSymbolicLinks: true,
      ...(pattern.globOptions || {}),
      cwd: pattern.context,
      onlyFiles: true,
    };

    // Will work when https://github.com/SuperchupuDev/tinyglobby/issues/81 will be resolved, so let's pass it to `tinyglobby` right now
    // @ts-ignore
    globOptions.fs = inputFileSystem;

    let glob;

    switch (fromType) {
      case "dir":
        compilation.contextDependencies.add(absoluteFrom);

        logger.debug(`added '${absoluteFrom}' as a context dependency`);

        pattern.context = absoluteFrom;
        glob = path.posix.join(
          getTinyGlobby().escapePath(
            getNormalizePath()(path.resolve(absoluteFrom)),
          ),
          "**/*",
        );
        absoluteFrom = path.join(absoluteFrom, "**/*");

        if (typeof globOptions.dot === "undefined") {
          globOptions.dot = true;
        }
        break;
      case "file":
        compilation.fileDependencies.add(absoluteFrom);

        logger.debug(`added '${absoluteFrom}' as a file dependency`);

        pattern.context = path.dirname(absoluteFrom);
        glob = getTinyGlobby().escapePath(
          getNormalizePath()(path.resolve(absoluteFrom)),
        );

        if (typeof globOptions.dot === "undefined") {
          globOptions.dot = true;
        }
        break;
      case "glob":
      default: {
        const contextDependencies = path.normalize(
          getGlobParent()(absoluteFrom),
        );

        compilation.contextDependencies.add(contextDependencies);

        logger.debug(`added '${contextDependencies}' as a context dependency`);

        glob = path.isAbsolute(originalFrom)
          ? originalFrom
          : path.posix.join(
              getTinyGlobby().escapePath(
                getNormalizePath()(path.resolve(pattern.context)),
              ),
              originalFrom,
            );
      }
    }

    logger.log(`begin globbing '${glob}'...`);

    /**
     * @type {string[]}
     */
    let globEntries;

    try {
      globEntries = await globby(glob, globOptions);
    } catch (error) {
      compilation.errors.push(/** @type {WebpackError} */ (error));

      return;
    }

    if (globEntries.length === 0) {
      if (pattern.noErrorOnMissing) {
        logger.log(
          `finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context to '${pattern.to}'`,
        );

        return;
      }

      const missingError = new Error(`unable to locate '${glob}' glob`);

      compilation.errors.push(/** @type {WebpackError} */ (missingError));

      return;
    }

    /**
     * @type {Array<CopiedResult | undefined>}
     */
    let copiedResult;

    try {
      copiedResult = await throttleAll(
        concurrency,
        globEntries.map((globEntry) => async () => {
          if (pattern.filter) {
            let isFiltered;

            try {
              isFiltered = await pattern.filter(globEntry);
            } catch (error) {
              compilation.errors.push(/** @type {WebpackError} */ (error));

              return;
            }

            if (!isFiltered) {
              logger.log(`skip '${globEntry}', because it was filtered`);

              return;
            }
          }

          const from = globEntry;

          logger.debug(`found '${from}'`);

          // `globby`/`fast-glob` return the relative path when the path contains special characters on windows
          const absoluteFilename = path.resolve(pattern.context, from);
          const to =
            typeof pattern.to === "function"
              ? await pattern.to({
                  context: pattern.context,
                  absoluteFilename,
                })
              : path.normalize(
                  typeof pattern.to !== "undefined" ? pattern.to : "",
                );
          const toType = pattern.toType
            ? pattern.toType
            : template.test(to)
              ? "template"
              : path.extname(to) === "" || to.slice(-1) === path.sep
                ? "dir"
                : "file";

          logger.log(`'to' option '${to}' determinated as '${toType}'`);

          const relativeFrom = path.relative(pattern.context, absoluteFilename);
          let filename = toType === "dir" ? path.join(to, relativeFrom) : to;

          if (path.isAbsolute(filename)) {
            filename = path.relative(
              /** @type {string} */ (compiler.options.output.path),
              filename,
            );
          }

          logger.log(`determined that '${from}' should write to '${filename}'`);

          const sourceFilename = getNormalizePath()(
            path.relative(compiler.context, absoluteFilename),
          );

          // If this came from a glob or dir, add it to the file dependencies
          if (fromType === "dir" || fromType === "glob") {
            compilation.fileDependencies.add(absoluteFilename);

            logger.debug(`added '${absoluteFilename}' as a file dependency`);
          }

          let cacheEntry;

          logger.debug(`getting cache for '${absoluteFilename}'...`);

          try {
            cacheEntry = await cache.getPromise(
              `${sourceFilename}|${index}`,
              null,
            );
          } catch (error) {
            compilation.errors.push(/** @type {WebpackError} */ (error));

            return;
          }

          /**
           * @type {Asset["source"] | undefined}
           */
          let source;

          if (cacheEntry) {
            logger.debug(`found cache for '${absoluteFilename}'...`);

            let isValidSnapshot;

            logger.debug(
              `checking snapshot on valid for '${absoluteFilename}'...`,
            );

            try {
              isValidSnapshot = await CopyPlugin.checkSnapshotValid(
                compilation,
                cacheEntry.snapshot,
              );
            } catch (error) {
              compilation.errors.push(/** @type {WebpackError} */ (error));

              return;
            }

            if (isValidSnapshot) {
              logger.debug(`snapshot for '${absoluteFilename}' is valid`);

              ({ source } = cacheEntry);
            } else {
              logger.debug(`snapshot for '${absoluteFilename}' is invalid`);
            }
          } else {
            logger.debug(`missed cache for '${absoluteFilename}'`);
          }

          if (!source) {
            const startTime = Date.now();

            logger.debug(`reading '${absoluteFilename}'...`);

            let data;

            try {
              // @ts-ignore
              data = await readFile(inputFileSystem, absoluteFilename);
            } catch (error) {
              compilation.errors.push(/** @type {WebpackError} */ (error));

              return;
            }

            logger.debug(`read '${absoluteFilename}'`);

            source = new RawSource(data);

            let snapshot;

            logger.debug(`creating snapshot for '${absoluteFilename}'...`);

            try {
              snapshot = await CopyPlugin.createSnapshot(
                compilation,
                startTime,
                absoluteFilename,
              );
            } catch (error) {
              compilation.errors.push(/** @type {WebpackError} */ (error));

              return;
            }

            if (snapshot) {
              logger.debug(`created snapshot for '${absoluteFilename}'`);
              logger.debug(`storing cache for '${absoluteFilename}'...`);

              try {
                await cache.storePromise(`${sourceFilename}|${index}`, null, {
                  source,
                  snapshot,
                });
              } catch (error) {
                compilation.errors.push(/** @type {WebpackError} */ (error));

                return;
              }

              logger.debug(`stored cache for '${absoluteFilename}'`);
            }
          }

          if (pattern.transform) {
            /**
             * @type {TransformerObject}
             */
            const transformObj =
              typeof pattern.transform === "function"
                ? { transformer: pattern.transform }
                : pattern.transform;

            if (transformObj.transformer) {
              logger.log(`transforming content for '${absoluteFilename}'...`);

              const buffer = source.buffer();

              if (transformObj.cache) {
                const hasher = compiler.webpack.util.createHash(
                  /** @type {string} */
                  (compilation.outputOptions.hashFunction),
                );

                const defaultCacheKeys = {
                  version,
                  sourceFilename,
                  transform: transformObj.transformer,
                  contentHash: hasher.update(buffer).digest("hex"),
                  index,
                };
                const cacheKeys = `transform|${getSerializeJavascript()(
                  typeof transformObj.cache === "boolean"
                    ? defaultCacheKeys
                    : typeof transformObj.cache.keys === "function"
                      ? await transformObj.cache.keys(
                          defaultCacheKeys,
                          absoluteFilename,
                        )
                      : { ...defaultCacheKeys, ...transformObj.cache.keys },
                )}`;

                logger.debug(
                  `getting transformation cache for '${absoluteFilename}'...`,
                );

                const cacheItem = cache.getItemCache(
                  cacheKeys,
                  cache.getLazyHashedEtag(source),
                );

                source = await cacheItem.getPromise();

                logger.debug(
                  source
                    ? `found transformation cache for '${absoluteFilename}'`
                    : `no transformation cache for '${absoluteFilename}'`,
                );

                if (!source) {
                  const transformed = await transformObj.transformer(
                    buffer,
                    absoluteFilename,
                  );

                  source = new RawSource(transformed);

                  logger.debug(
                    `caching transformation for '${absoluteFilename}'...`,
                  );

                  await cacheItem.storePromise(source);

                  logger.debug(
                    `cached transformation for '${absoluteFilename}'`,
                  );
                }
              } else {
                source = new RawSource(
                  await transformObj.transformer(buffer, absoluteFilename),
                );
              }
            }
          }

          let info =
            typeof pattern.info === "undefined"
              ? {}
              : typeof pattern.info === "function"
                ? pattern.info({
                    absoluteFilename,
                    sourceFilename,
                    filename,
                    toType,
                  }) || {}
                : pattern.info || {};

          if (toType === "template") {
            logger.log(
              `interpolating template '${filename}' for '${sourceFilename}'...`,
            );

            const contentHash = CopyPlugin.getContentHash(
              compiler,
              compilation,
              source.buffer(),
            );
            const ext = path.extname(sourceFilename);
            const base = path.basename(sourceFilename);
            const name = base.slice(0, base.length - ext.length);
            const data = {
              filename: getNormalizePath()(
                path.relative(pattern.context, absoluteFilename),
              ),
              contentHash,
              chunk: {
                name,
                id: /** @type {string} */ (sourceFilename),
                hash: contentHash,
              },
            };
            const { path: interpolatedFilename, info: assetInfo } =
              compilation.getPathWithInfo(getNormalizePath()(filename), data);

            info = { ...info, ...assetInfo };
            filename = interpolatedFilename;

            logger.log(
              `interpolated template '${filename}' for '${sourceFilename}'`,
            );
          } else {
            filename = getNormalizePath()(filename);
          }

          // eslint-disable-next-line consistent-return
          return {
            sourceFilename,
            absoluteFilename,
            filename,
            source,
            info,
            force: pattern.force,
          };
        }),
      );
    } catch (error) {
      compilation.errors.push(/** @type {WebpackError} */ (error));

      return;
    }

    if (copiedResult.length === 0) {
      if (pattern.noErrorOnMissing) {
        logger.log(
          `finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context to '${pattern.to}'`,
        );

        return;
      }

      const missingError = new Error(
        `unable to locate '${glob}' glob after filtering paths`,
      );

      compilation.errors.push(/** @type {WebpackError} */ (missingError));

      return;
    }

    logger.log(
      `finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context`,
    );

    // eslint-disable-next-line consistent-return
    return copiedResult;
  }
@private
@param {typeof import("tinyglobby").glob} globby
@param {Compiler} compiler
@param {Compilation} compilation
@param {WebpackLogger} logger
@param {CacheFacade} cache
@param {number} concurrency
@param {ObjectPattern & { context: string }} inputPattern
@param {number} index
@returns {Promise<Array<CopiedResult | undefined> | undefined>}