Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions lib/src/commands/packages/commands/check/commands/licenses.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,23 @@ import 'package:package_config/package_config.dart' as package_config;
import 'package:pana/src/license_detection/license_detector.dart' as detector;
import 'package:path/path.dart' as path;
import 'package:very_good_cli/src/pub_license/spdx_license.gen.dart';
import 'package:very_good_cli/src/pubspec/pubspec.dart';
import 'package:very_good_cli/src/pubspec_lock/pubspec_lock.dart';
import 'package:very_good_cli/src/pubspec_workspace/pubspec_workspace.dart';

/// Overrides the [package_config.findPackageConfig] function for testing.
@visibleForTesting
Future<package_config.PackageConfig?> Function(Directory directory)?
findPackageConfigOverride;

/// Overrides the [resolveWorkspaceDependencies] function for testing.
@visibleForTesting
Map<String, PubspecDependencyType>? Function(
Directory directory, {
required Logger logger,
})?
resolveWorkspaceOverride;

/// Overrides the [detector.detectLicense] function for testing.
@visibleForTesting
Future<detector.Result> Function(String, double)? detectLicenseOverride;
Expand Down Expand Up @@ -181,7 +191,15 @@ class PackagesCheckLicensesCommand extends Command<int> {
final pubspecLockFile = File(path.join(targetPath, pubspecLockBasename));
if (!pubspecLockFile.existsSync()) {
progress.cancel();
_logger.err('Could not find a $pubspecLockBasename in $targetPath');
if (declaresWorkspaceResolution(targetDirectory)) {
_logger.err(
'Could not find a $pubspecLockBasename in $targetPath.\n'
'This package resolves as part of a Pub workspace. '
'Run the command from the workspace root instead.',
);
} else {
_logger.err('Could not find a $pubspecLockBasename in $targetPath');
}
return ExitCode.noInput.code;
}

Expand All @@ -192,21 +210,26 @@ class PackagesCheckLicensesCommand extends Command<int> {
return ExitCode.noInput.code;
}

final resolveWorkspace =
resolveWorkspaceOverride ?? resolveWorkspaceDependencies;
final workspaceDeps = resolveWorkspace(targetDirectory, logger: _logger);

final filteredDependencies = pubspecLock.packages.where((dependency) {
if (!dependency.isPubHosted) return false;

if (skippedPackages.contains(dependency.name)) return false;

final dependencyType = dependency.type;
final dependencyType = workspaceDeps == null
? dependency.type
: workspaceDeps[dependency.name] ?? PubspecDependencyType.transitive;
return (dependencyTypes.contains('direct-main') &&
dependencyType == PubspecLockPackageDependencyType.directMain) ||
dependencyType == PubspecDependencyType.directMain) ||
(dependencyTypes.contains('direct-dev') &&
dependencyType == PubspecLockPackageDependencyType.directDev) ||
dependencyType == PubspecDependencyType.directDev) ||
(dependencyTypes.contains('transitive') &&
dependencyType == PubspecLockPackageDependencyType.transitive) ||
dependencyType == PubspecDependencyType.transitive) ||
(dependencyTypes.contains('direct-overridden') &&
dependencyType ==
PubspecLockPackageDependencyType.directOverridden);
dependencyType == PubspecDependencyType.directOverridden);
});

if (filteredDependencies.isEmpty) {
Expand Down
87 changes: 87 additions & 0 deletions lib/src/pubspec/pubspec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/// Shared pubspec-domain primitives built on top of `package:pubspec_parse`.
///
/// The `packages check licenses` command reads dependency information from two
/// different sources: a `pubspec.lock` file (via `pubspec_lock.dart`) and the
/// `pubspec.yaml` files of a Pub workspace (via `pubspec_workspace.dart`). Both
/// classify dependencies with the same model, so that model lives here — a
/// common ancestor both import — instead of being duplicated across the two
/// sibling parsers.
library;

import 'dart:io';
import 'package:pubspec_parse/pubspec_parse.dart';

export 'package:pubspec_parse/pubspec_parse.dart';

/// {@template pubspec_dependency_type}
/// The classification of a package dependency.
/// {@endtemplate}
enum PubspecDependencyType {
/// Another package that your package needs to work.
///
/// See also:
///
/// * [Dart's dependency documentation](https://dart.dev/tools/pub/dependencies)
directMain._('direct main'),

/// Another package that your package needs during development.
///
/// See also:
///
/// * [Dart's developer dependency documentation](https://dart.dev/tools/pub/dependencies#dev-dependencies)
directDev._('direct dev'),

/// A dependency that your package indirectly uses because one of its
/// dependencies requires it.
///
/// See also:
///
/// * [Dart's transitive dependency documentation](https://dart.dev/tools/pub/glossary#transitive-)
transitive._('transitive'),

/// A dependency that your package overrides that is not already a
/// `direct main` or `direct dev` dependency.
///
/// See also:
///
/// * [Dart's dependency override documentation](https://dart.dev/tools/pub/dependencies#dependency-overrides)
directOverridden._('direct overridden');

const PubspecDependencyType._(this.value);

/// Parses a [PubspecDependencyType] from its `pubspec.lock` textual form.
///
/// Throws an [ArgumentError] if the string is not a valid dependency type.
factory PubspecDependencyType.parse(String value) {
if (_valueMap.containsKey(value)) return _valueMap[value]!;

throw ArgumentError.value(
value,
'value',
'Invalid PubspecDependencyType value.',
);
}

static final Map<String, PubspecDependencyType> _valueMap = {
for (final type in PubspecDependencyType.values) type.value: type,
};

/// The textual representation of the [PubspecDependencyType] as it appears in
/// the `dependency` field of a `pubspec.lock` file.
final String value;
}

/// Tolerantly parses a [Pubspec] from [pubspecFile].
///
/// Returns `null` when the file does not exist or cannot be parsed. Parsing is
/// lenient so valid-but-unmodeled keys (e.g. a `flutter:` block) do not throw.
Pubspec? tryParsePubspec(File pubspecFile) {
if (!pubspecFile.existsSync()) return null;
try {
return Pubspec.parse(pubspecFile.readAsStringSync(), lenient: true);
// Tolerate any malformed pubspec by returning null instead of throwing.
// ignore: avoid_catches_without_on_clauses
} catch (_) {
return null;
}
}
68 changes: 4 additions & 64 deletions lib/src/pubspec_lock/pubspec_lock.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ library;
import 'dart:collection';

import 'package:equatable/equatable.dart';
import 'package:very_good_cli/src/pubspec/pubspec.dart';
import 'package:yaml/yaml.dart';

/// {@template PubspecLockParseException}
Expand Down Expand Up @@ -88,7 +89,7 @@ class PubspecLockPackage extends Equatable {
required YamlMap data,
}) {
final dependency = data['dependency'] as String;
final dependencyType = PubspecLockPackageDependencyType.parse(dependency);
final dependencyType = PubspecDependencyType.parse(dependency);

final source = data['source'] as String;
late final bool isPubHosted;
Expand All @@ -110,73 +111,12 @@ class PubspecLockPackage extends Equatable {
/// The name of the dependency.
final String name;

/// {@macro PubspecLockDependencyType}
final PubspecLockPackageDependencyType type;
/// {@macro pubspec_dependency_type}
final PubspecDependencyType type;

/// Whether the dependency is hosted on pub.dev or not.
final bool isPubHosted;

@override
List<Object?> get props => [type, isPubHosted];
}

/// {@template PubspecLockDependencyType}
/// The type of a [PubspecLockPackage].
/// {@endtemplate}
enum PubspecLockPackageDependencyType {
/// Another package that your package needs to work.
///
/// See also:
///
/// * [Dart's dependency documentation](https://dart.dev/tools/pub/dependencies)
directMain._('direct main'),

/// Another package that your package needs during development.
///
/// See also:
///
/// * [Dart's developer dependency documentation](https://dart.dev/tools/pub/dependencies#dev-dependencies)
directDev._('direct dev'),

/// A dependency that your package indirectly uses because one of its
/// dependencies requires it.
///
/// See also:
///
/// * [Dart's transitive dependency documentation](https://dart.dev/tools/pub/glossary#transitive-)
transitive._('transitive'),

/// A dependency that your package overrides that is not already a
/// `direct main` or `direct dev` dependency.
///
/// See also:
///
/// * [Dart's dependency override documentation](https://dart.dev/tools/pub/dependencies#dependency-overrides)
directOverridden._('direct overridden');

const PubspecLockPackageDependencyType._(this.value);

/// Parses a [PubspecLockPackageDependencyType] from a string.
///
/// Throws an [ArgumentError] if the string is not a valid dependency type.
factory PubspecLockPackageDependencyType.parse(String value) {
if (_valueMap.containsKey(value)) {
return _valueMap[value]!;
}

throw ArgumentError.value(
value,
'value',
'Invalid PubspecLockPackageDependencyType value.',
);
}

static Map<String, PubspecLockPackageDependencyType> _valueMap = {
for (final type in PubspecLockPackageDependencyType.values)
type.value: type,
};

/// The string representation of the [PubspecLockPackageDependencyType]
/// as it appears in a pubspec.lock file.
final String value;
}
Loading
Loading