collectFiles static method

(Set<String>, Set<String>) collectFiles({
  1. required CommandLogger logger,
  2. required Directory rootDirectory,
  3. String beneath = '.',
})

Collects all files in the project directory that are not ignored by any ignore rules files in the project directory.

The ignore rules are determined by the presence of ProjectZipper.recognizedIgnoreRuleFiles files.

The beneath parameter contains the relative path below the rootDirectory to collect files from. The default value is '.', which means that all files in the rootDirectory will be collected.

Implementation

static (Set<String> collectedFiles, Set<String> ignoredFiles) collectFiles({
  required final CommandLogger logger,
  required final Directory rootDirectory,
  final String beneath = '.',
}) {
  final uri = p.toUri(p.normalize(rootDirectory.path));

  var root = uri.path;
  // On Windows, uri.path causes absolute paths on a drive to be prefixed with a slash, remove it.
  if (_windowsSlashDrivePattern.hasMatch(root)) {
    root = root.substring(1);
  }

  final (collectedFiles, ignoredFiles) = Ignore.listFiles(
    beneath: beneath,
    listDir: (final dir) {
      final contents = Directory(_resolve(from: root, path: dir))
          .listSync(recursive: true);
      return contents.map((final entity) {
        final path = entity.path;
        if (_linkExists(path)) {
          final target = Link(path).targetSync();
          if (_dirExists(path)) {
            throw DirectorySymLinkException(path);
          }
          if (!_fileExists(entity.path)) {
            throw NonResolvingSymlinkException(path, target);
          }
        }
        final relative = p.relative(entity.path, from: root);
        if (Platform.isWindows) {
          return p.posix.joinAll(p.split(relative));
        }
        return relative;
      });
    },
    ignoreForDir: (final dir) {
      final ignoreRuleFiles = ProjectZipper.recognizedIgnoreRuleFiles.map(
        (final fileName) => _resolve(from: root, path: '$dir/$fileName'),
      );

      final rules = [
        ...ProjectZipper.defaultIgnoreRules,
        ...ignoreRuleFiles.map((final filePath) {
          if (!_fileExists(filePath)) return null;

          return _readTextFile(filePath);
        }).nonNulls,
      ];

      if (rules.isEmpty) return null;

      return Ignore(
        rules,
        onInvalidPattern: (final pattern, final exception) {
          logger.warning(
            'Ignoring invalid pattern in ignore file: $pattern. Remove it to avoid this warning.',
          );
        },
        // Ignore case on macOS and Windows, because `git clone` and
        // `git init` will set `core.ignoreCase = true` in the local
        // local `.git/config` file for the repository.
        //
        // So on Windows and macOS most users will have case-insensitive
        // behavior with `.gitignore`, hence, it seems reasonable to do
        // the same when we interpret `.gitignore` and `.scloudignore`.
        //
        // There are cases where a user may have case-sensitive behavior
        // with `.gitignore` on Windows and macOS:
        //
        //  (A) The user has manually overwritten the repository
        //      configuration setting `core.ignoreCase = false`.
        //
        //  (B) The git-clone or git-init command that create the
        //      repository did not deem `core.ignoreCase = true` to be
        //      appropriate. Documentation for [git-config]][1] implies
        //      this might depend on whether or not the filesystem is
        //      case sensitive:
        //      > If true, this option enables various workarounds to
        //      > enable Git to work better on filesystems that are not
        //      > case sensitive, like FAT.
        //      > ...
        //      > The default is false, except git-clone[1] or
        //      > git-init[1] will probe and set core.ignoreCase true
        //      > if appropriate when the repository is created.
        //
        // In either case, it seems likely that users on Windows and
        // macOS will prefer case-insensitive matching. We specifically
        // know that some tooling will generate `.PDB` files instead of
        // `.pdb`, see: [#3003][2]
        //
        // [1]: https://git-scm.com/docs/git-config/2.14.6#Documentation/git-config.txt-coreignoreCase
        // [2]: https://github.com/dart-lang/pub/issues/3003
        ignoreCase: Platform.isMacOS || Platform.isWindows,
      );
    },
    isDir: (final dir) => _dirExists(_resolve(from: root, path: dir)),
  );

  final collected = collectedFiles
      .map((final file) => _resolve(from: root, path: file))
      .toSet();

  final ignored = ignoredFiles
      .map((final file) => _resolve(from: root, path: file))
      .toSet();

  return (collected, ignored);
}