resolvePackages static method

Future<PackageMap> resolvePackages({
  1. required String workspacePath,
  2. required List<Glob> packages,
  3. required List<Glob> ignore,
  4. required MelosLogger logger,
})

Implementation

static Future<PackageMap> resolvePackages({
  required String workspacePath,
  required List<Glob> packages,
  required List<Glob> ignore,
  required MelosLogger logger,
}) async {
  final packageMap = <String, Package>{};

  final dartToolGlob =
      createGlob('**/.dart_tool', currentDirectoryPath: workspacePath);
  // Flutter symlinked plugins for iOS/macOS should not be included in the package list.
  final symlinksPluginsGlob = createGlob(
    '**/.symlinks/plugins',
    currentDirectoryPath: workspacePath,
  );
  // Flutter version manager should not be included in the package list.
  final fvmGlob = createGlob(
    '**/.fvm',
    currentDirectoryPath: workspacePath,
  );
  // Ephemeral plugin symlinked packages should not be included in the package
  // list.
  final pluginSymlinksGlob = createGlob(
    '**/.plugin_symlinks',
    currentDirectoryPath: workspacePath,
  );

  final pubspecsByResolvedPath =
      HashMap<String, File>(equals: p.equals, hashCode: p.hash);

  Stream<FileSystemEntity> allWorkspaceEntities() async* {
    final workspaceDir = Directory(workspacePath);
    yield workspaceDir;
    yield* workspaceDir.listConditionallyRecursive(
      recurseCondition: (dir) {
        final path = dir.path;
        return !dartToolGlob.matches(path) &&
            !symlinksPluginsGlob.matches(path) &&
            !fvmGlob.matches(path) &&
            !pluginSymlinksGlob.matches(path);
      },
    );
  }

  await for (final entity in allWorkspaceEntities()) {
    final path = entity.path;
    late final isIncluded = packages.any((glob) => glob.matches(path)) &&
        !ignore.any((glob) => glob.matches(path));

    if (entity is File && p.basename(path) == 'pubspec.yaml' && isIncluded) {
      final resolvedPath = await entity.resolveSymbolicLinks();
      pubspecsByResolvedPath[resolvedPath] = entity;
    } else if (entity is Directory &&
        isPackageDirectory(entity.path) &&
        isIncluded) {
      final pubspecPath = p.join(path, 'pubspec.yaml');
      pubspecsByResolvedPath[pubspecPath] = File(pubspecPath);
    }
  }

  final allPubspecs = pubspecsByResolvedPath.values;

  await Future.wait<void>(
    allPubspecs.map((pubspecFile) async {
      final pubspecDirPath = pubspecFile.parent.path;
      final pubSpec = await PubSpec.load(pubspecFile.parent);

      final name = pubSpec.name!;

      if (packageMap.containsKey(name)) {
        throw MelosConfigException(
          '''
Multiple packages with the name `$name` found in the workspace, which is unsupported.
To fix this problem, consider renaming your packages to have a unique name.

The packages that caused the problem are:
- $name at ${printablePath(relativePath(pubspecDirPath, workspacePath))}
- $name at ${printablePath(relativePath(packageMap[name]!.path, workspacePath))}
''',
        );
      }

      packageMap[name] = Package(
        name: name,
        path: pubspecDirPath,
        pathRelativeToWorkspace: relativePath(pubspecDirPath, workspacePath),
        version: pubSpec.version ?? Version.none,
        publishTo: pubSpec.publishTo,
        packageMap: packageMap,
        dependencies: pubSpec.dependencies.keys.toList(),
        devDependencies: pubSpec.devDependencies.keys.toList(),
        dependencyOverrides: pubSpec.dependencyOverrides.keys.toList(),
        pubSpec: pubSpec,
      );
    }),
  );

  return PackageMap(packageMap, logger);
}