Skip to content

Commit

Permalink
fix: report all dependencies in list command (#313)
Browse files Browse the repository at this point in the history
Fixes #243
  • Loading branch information
blaugold authored Jun 6, 2022
1 parent ff5bfbe commit bb76d3a
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 18 deletions.
13 changes: 10 additions & 3 deletions packages/melos/lib/src/commands/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ mixin _ListMixin on _Melos {
void _listGraph(MelosWorkspace workspace) {
final jsonGraph = <String, List<String>>{};
for (final package in workspace.filteredPackages.values) {
jsonGraph[package.name] = package.dependenciesInWorkspace.keys.toList();
jsonGraph[package.name] =
package.allDependenciesInWorkspace.keys.toList();
}

const encoder = JsonEncoder.withIndent(' ');
Expand Down Expand Up @@ -139,8 +140,8 @@ mixin _ListMixin on _Melos {
'flutter_package': package.isFlutterPackage,
'flutter_app': package.isFlutterApp,
'flutter_plugin': package.isFlutterPlugin,
'dependencies': package.dependenciesInWorkspace.keys.toList(),
'dependents': package.dependentsInWorkspace.keys.toList(),
'dependencies': package.allDependenciesInWorkspace.keys.toList(),
'dependents': package.allDependentsInWorkspace.keys.toList(),
});

if (package.isFlutterApp) {
Expand Down Expand Up @@ -220,6 +221,12 @@ mixin _ListMixin on _Melos {
' ${package.name} -> ${dep.name} [style="dashed"; color="${getColor(dep.name)}"];',
);
}

for (final dep in package.dependencyOverridesInWorkspace.values) {
buffer.add(
' ${package.name} -> ${dep.name} [style="dotted"; color="${getColor(dep.name)}"];',
);
}
}

final groupedPackages = workspace.filteredPackages.values
Expand Down
30 changes: 19 additions & 11 deletions packages/melos/lib/src/package.dart
Original file line number Diff line number Diff line change
Expand Up @@ -667,37 +667,45 @@ class Package {
late final allDependentsInWorkspace = {
...dependentsInWorkspace,
...devDependentsInWorkspace,
...dependentsOverridesInWorkspace,
};

/// The dependencies listen in `dev_dependencies:` inside the package's `pubspec.yaml`
/// that are part of the melos workspace
late final Map<String, Package> devDependenciesInWorkspace =
_packagesInWorkspaceForNames(devDependencies);

/// The dependencies listen in `dependencies:` inside the package's `pubspec.yaml`
/// that are part of the melos workspace
/// The dependencies listed in `dependencies:` inside the package's
/// `pubspec.yaml` that are part of the melos workspace
late final Map<String, Package> dependenciesInWorkspace =
_packagesInWorkspaceForNames(dependencies);

/// The dependencies listen in `dependency_overrides:` inside the package's `pubspec.yaml`
/// that are part of the melos workspace
/// The dependencies listed in `dev_dependencies:` inside the package's
/// `pubspec.yaml` that are part of the melos workspace
late final Map<String, Package> devDependenciesInWorkspace =
_packagesInWorkspaceForNames(devDependencies);

/// The dependencies listed in `dependency_overrides:` inside the package's
/// `pubspec.yaml` that are part of the melos workspace
late final Map<String, Package> dependencyOverridesInWorkspace =
_packagesInWorkspaceForNames(dependencyOverrides);

/// The packages that depends on this package.
/// The packages that depend on this package as a dependency.
late final Map<String, Package> dependentsInWorkspace = {
for (final entry in _packageMap.entries)
if (entry.value.dependenciesInWorkspace.containsKey(name))
entry.key: entry.value,
};

/// The packages that depends on this package.
/// The packages that depend on this package as a dev dependency..
late final Map<String, Package> devDependentsInWorkspace = {
for (final entry in _packageMap.entries)
if (entry.value.devDependenciesInWorkspace.containsKey(name))
entry.key: entry.value,
};

/// The packages that depend on this package as a dependency override.
late final Map<String, Package> dependentsOverridesInWorkspace = {
for (final entry in _packageMap.entries)
if (entry.value.dependencyOverridesInWorkspace.containsKey(name))
entry.key: entry.value,
};

late final Map<String, Package> allTransitiveDependenciesInWorkspace =
_transitivelyRelatedPackages(
root: this,
Expand Down
160 changes: 160 additions & 0 deletions packages/melos/test/commands/list_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:convert';

import 'package:melos/src/commands/runner.dart';
import 'package:melos/src/common/glob.dart';
import 'package:melos/src/common/platform.dart';
Expand Down Expand Up @@ -145,7 +147,9 @@ long_name 0.0.0 packages/long_name PRIVATE
);
}),
);
});

group('parsable', () {
test(
'relativePaths flag prints relative paths only if true',
withMockFs(() async {
Expand Down Expand Up @@ -209,5 +213,161 @@ packages/c
skip: currentPlatform.isWindows,
);
});

group('graph', () {
test(
'reports all dependencies in workspace',
withMockFs(() async {
final workspaceDir = createMockWorkspaceFs(
packages: [
MockPackageFs(name: 'a'),
MockPackageFs(name: 'b'),
MockPackageFs(name: 'c'),
MockPackageFs(
name: 'd',
dependencies: ['a'],
devDependencies: ['b'],
dependencyOverrides: ['c'],
),
],
);

final config = await MelosWorkspaceConfig.fromDirectory(workspaceDir);
final melos = Melos(logger: logger, config: config);
await melos.list(
kind: ListOutputKind.graph,
);

expect(
logger.output,
'''
{
"a": [],
"b": [],
"c": [],
"d": [
"a",
"b",
"c"
]
}
''',
);
}),
);
});

group('json', () {
test(
'reports all dependencies in workspace',
withMockFs(() async {
final workspaceDir = createMockWorkspaceFs(
packages: [
MockPackageFs(name: 'a'),
MockPackageFs(name: 'b'),
MockPackageFs(name: 'c'),
MockPackageFs(
name: 'd',
dependencies: ['a'],
devDependencies: ['b'],
dependencyOverrides: ['c'],
),
],
);

final config = await MelosWorkspaceConfig.fromDirectory(workspaceDir);
final melos = Melos(logger: logger, config: config);
await melos.list(
kind: ListOutputKind.json,
long: true,
);
final json = (jsonDecode(logger.output) as List<Object?>)
.cast<Map<String, Object?>>();

expect(
json.map(
(pkg) => {
'name': pkg['name'],
'dependencies': pkg['dependencies'],
'dependents': pkg['dependents'],
},
),
[
{
'name': 'a',
'dependencies': <String>[],
'dependents': ['d'],
},
{
'name': 'b',
'dependencies': <String>[],
'dependents': ['d'],
},
{
'name': 'c',
'dependencies': <String>[],
'dependents': ['d'],
},
{
'name': 'd',
'dependencies': ['a', 'b', 'c'],
'dependents': <String>[],
},
],
);
}),
);
});

group('gviz', () {
test(
'reports all dependencies in workspace',
withMockFs(() async {
final workspaceDir = createMockWorkspaceFs(
packages: [
MockPackageFs(name: 'a'),
MockPackageFs(name: 'b'),
MockPackageFs(name: 'c'),
MockPackageFs(
name: 'd',
dependencies: ['a'],
devDependencies: ['b'],
dependencyOverrides: ['c'],
),
],
);

final config = await MelosWorkspaceConfig.fromDirectory(workspaceDir);
final melos = Melos(logger: logger, config: config);
await melos.list(
kind: ListOutputKind.gviz,
);

expect(
logger.output,
'''
digraph packages {
size="10"; ratio=fill;
a [shape="box"; color="#ff5307"];
b [shape="box"; color="#e03cc2"];
c [shape="box"; color="#fa533c"];
d [shape="box"; color="#80dce6"];
d -> a [style="filled"; color="#ff5307"];
d -> b [style="dashed"; color="#e03cc2"];
d -> c [style="dotted"; color="#fa533c"];
subgraph "cluster packages" {
label="packages";
color="#6b4949";
a;
b;
c;
d;
}
}
''',
);
}),
);
});
});
}
33 changes: 29 additions & 4 deletions packages/melos/test/mock_workspace_fs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ void _createPackage(MockPackageFs package, String workspaceRoot) {
'''
dependencies:
${_yamlMap(package.dependencyMap, indent: 2)}
dev_dependencies:
${_yamlMap(package.devDependencyMap, indent: 2)}
dependency_overrides:
${_yamlMap(package.dependencyOverridesMap, indent: 2)}
''',
);

Expand All @@ -129,11 +135,15 @@ class MockPackageFs {
required this.name,
String? path,
List<String>? dependencies,
List<String>? devDependencies,
List<String>? dependencyOverrides,
this.version,
this.publishToNone = false,
bool generateExample = false,
}) : _path = path,
dependencies = dependencies ?? const [],
devDependencies = devDependencies ?? const [],
dependencyOverrides = dependencyOverrides ?? const [],
createExamplePackage = generateExample;

/// Name of the package (must be a valid Dart package name)
Expand All @@ -148,13 +158,28 @@ class MockPackageFs {
/// `true` if this package's yaml has a `publish_to: none` setting.
final bool publishToNone;

/// A list of package names this one depends on
/// A list of package names that are dependencies of this one.
final List<String> dependencies;

/// A list of package names that are dev dependencies of this one.
final List<String> devDependencies;

/// A list of package names that are dependency overrides of this one.
final List<String> dependencyOverrides;

/// A mapping of dependency names to their versions (always "any")
Map<String, String> get dependencyMap {
return Map.fromEntries(dependencies.map((name) => MapEntry(name, 'any')));
}
Map<String, String> get dependencyMap =>
Map.fromEntries(dependencies.map((name) => MapEntry(name, 'any')));

/// A mapping of dev dependency names to their versions (always "any")
Map<String, String> get devDependencyMap => Map.fromEntries(
devDependencies.map((name) => MapEntry(name, 'any')),
);

/// A mapping of dependency overrides names to their versions (always "any")
Map<String, String> get dependencyOverridesMap => Map.fromEntries(
dependencyOverrides.map((name) => MapEntry(name, 'any')),
);

/// `true` if an example package should be generated
final bool createExamplePackage;
Expand Down

0 comments on commit bb76d3a

Please sign in to comment.