Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Use the pubspec_parse package for parsing #794

Merged
merged 5 commits into from
Nov 20, 2024
Merged
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
3 changes: 1 addition & 2 deletions melos.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: Melos
repository: https://github.com/invertase/melos
packages:
- .
- packages/*
ignore:
- packages/melos_flutter_deps_check
Expand Down Expand Up @@ -32,7 +31,7 @@ command:
prompts: ^2.0.0
pub_semver: ^2.1.4
pub_updater: ^0.4.0
pubspec: ^2.3.0
pubspec_parse: ^1.3.0
string_scanner: ^1.2.0
yaml: ^3.1.2
yaml_edit: ^2.1.1
Expand Down
69 changes: 23 additions & 46 deletions packages/melos/lib/src/command_configs/bootstrap.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:collection/collection.dart';
import 'package:glob/glob.dart';
import 'package:meta/meta.dart';
import 'package:pubspec/pubspec.dart';
import 'package:pubspec_parse/pubspec_parse.dart';

import '../common/extensions/dependency.dart';
import '../common/extensions/environment.dart';
import '../common/glob.dart';
import '../common/glob_equality.dart';
import '../common/utils.dart';
import '../common/validation.dart';
import '../lifecycle_hooks/lifecycle_hooks.dart';

Expand Down Expand Up @@ -48,29 +49,11 @@ class BootstrapCommandConfigs {
) ??
false;

final environment = assertKeyIsA<Map<Object?, Object?>?>(
key: 'environment',
map: yaml,
).let(Environment.fromJson);

final dependencies = assertKeyIsA<Map<Object?, Object?>?>(
key: 'dependencies',
map: yaml,
)?.map(
(key, value) => MapEntry(
key.toString(),
DependencyReference.fromJson(value),
),
);

final devDependencies = assertKeyIsA<Map<Object?, Object?>?>(
key: 'dev_dependencies',
map: yaml,
)?.map(
(key, value) => MapEntry(
key.toString(),
DependencyReference.fromJson(value),
),
// Create a dummy pubspec name to be able to extract the constraints using
// the pubspec parser.
final bootstrapConstraints = Pubspec.fromJson(
{'name': 'bootstrap', ...yaml},
lenient: true,
);

final dependencyOverridePaths = assertListIsA<String>(
Expand All @@ -93,13 +76,17 @@ class BootstrapCommandConfigs {
? LifecycleHooks.fromYaml(hooksMap, workspacePath: workspacePath)
: LifecycleHooks.empty;

final environment = bootstrapConstraints.environment ?? {};
final dependencies = bootstrapConstraints.dependencies;
final devDependencies = bootstrapConstraints.devDependencies;

return BootstrapCommandConfigs(
runPubGetInParallel: runPubGetInParallel,
runPubGetOffline: runPubGetOffline,
enforceLockfile: enforceLockfile,
environment: environment,
dependencies: dependencies,
devDependencies: devDependencies,
environment: environment.isEmpty ? null : environment,
dependencies: dependencies.isEmpty ? null : dependencies,
devDependencies: devDependencies.isEmpty ? null : devDependencies,
dependencyOverridePaths: dependencyOverridePaths
.map(
(override) =>
Expand Down Expand Up @@ -134,10 +121,10 @@ class BootstrapCommandConfigs {
final Environment? environment;

/// Dependencies to be synced between all packages.
final Map<String, DependencyReference>? dependencies;
final Map<String, Dependency>? dependencies;

/// Dev dependencies to be synced between all packages.
final Map<String, DependencyReference>? devDependencies;
final Map<String, Dependency>? devDependencies;

/// A list of [Glob]s for paths that contain packages to be used as dependency
/// overrides for all packages managed in the Melos workspace.
Expand All @@ -152,14 +139,9 @@ class BootstrapCommandConfigs {
'runPubGetOffline': runPubGetOffline,
'enforceLockfile': enforceLockfile,
if (environment != null) 'environment': environment!.toJson(),
if (dependencies != null)
'dependencies': dependencies!.map(
(key, value) => MapEntry(key, value.toJson()),
),
if (dependencies != null) 'dependencies': dependencies!.toJson(),
if (devDependencies != null)
'dev_dependencies': devDependencies!.map(
(key, value) => MapEntry(key, value.toJson()),
),
'dev_dependencies': devDependencies!.toJson(),
if (dependencyOverridePaths.isNotEmpty)
'dependencyOverridePaths':
dependencyOverridePaths.map((path) => path.toString()).toList(),
Expand All @@ -175,11 +157,8 @@ class BootstrapCommandConfigs {
other.runPubGetOffline == runPubGetOffline &&
other.enforceLockfile == enforceLockfile &&
// Extracting equality from environment here as it does not implement ==
other.environment?.sdkConstraint == environment?.sdkConstraint &&
const DeepCollectionEquality().equals(
other.environment?.unParsedYaml,
environment?.unParsedYaml,
) &&
other.environment.sdkConstraint == environment.sdkConstraint &&
const DeepCollectionEquality().equals(other.environment, environment) &&
const DeepCollectionEquality().equals(other.dependencies, dependencies) &&
const DeepCollectionEquality()
.equals(other.devDependencies, devDependencies) &&
Expand All @@ -195,10 +174,8 @@ class BootstrapCommandConfigs {
enforceLockfile.hashCode ^
// Extracting hashCode from environment here as it does not implement
// hashCode
(environment?.sdkConstraint).hashCode ^
const DeepCollectionEquality().hash(
environment?.unParsedYaml,
) ^
environment.sdkConstraint.hashCode ^
const DeepCollectionEquality().hash(environment) ^
const DeepCollectionEquality().hash(dependencies) ^
const DeepCollectionEquality().hash(devDependencies) ^
const DeepCollectionEquality(GlobEquality())
Expand Down
91 changes: 41 additions & 50 deletions packages/melos/lib/src/commands/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,29 +156,30 @@ mixin _BootstrapMixin on _CleanMixin {
) async {
final allTransitiveDependencies =
package.allTransitiveDependenciesInWorkspace;
final melosDependencyOverrides = <String, DependencyReference>{};
final melosDependencyOverrides = <String, Dependency>{};

// Traversing all packages so that transitive dependencies for the
// bootstrapped packages are setup properly.
for (final otherPackage in workspace.allPackages.values) {
if (allTransitiveDependencies.containsKey(otherPackage.name)) {
melosDependencyOverrides[otherPackage.name] =
PathReference(utils.relativePath(otherPackage.path, package.path));
melosDependencyOverrides[otherPackage.name] = PathDependency(
utils.relativePath(otherPackage.path, package.path),
);
}
}

// Add custom workspace overrides.
for (final dependencyOverride
in workspace.dependencyOverridePackages.values) {
melosDependencyOverrides[dependencyOverride.name] = PathReference(
melosDependencyOverrides[dependencyOverride.name] = PathDependency(
utils.relativePath(dependencyOverride.path, package.path),
);
}

// Add existing dependency overrides from pubspec.yaml last, overwriting
// overrides that would be made by Melos, to provide granular control at a
// package level.
melosDependencyOverrides.addAll(package.pubSpec.dependencyOverrides);
melosDependencyOverrides.addAll(package.pubspec.dependencyOverrides);

// Load current pubspec_overrides.yaml.
final pubspecOverridesFile =
Expand Down Expand Up @@ -262,9 +263,9 @@ mixin _BootstrapMixin on _CleanMixin {

Future<void> _setSharedDependenciesForPackage(
Package package, {
required Environment? environment,
required Map<String, DependencyReference>? dependencies,
required Map<String, DependencyReference>? devDependencies,
required Map<String, VersionConstraint?>? environment,
required Map<String, Dependency>? dependencies,
required Map<String, Dependency>? devDependencies,
}) async {
final packagePubspecFile = utils.pubspecPathForDirectory(package.path);
final packagePubspecContents = await readTextFileAsync(packagePubspecFile);
Expand All @@ -273,20 +274,20 @@ mixin _BootstrapMixin on _CleanMixin {
final updatedEnvironment = _updateEnvironment(
pubspecEditor: pubspecEditor,
workspaceEnvironment: environment,
packageEnvironment: package.pubSpec.environment,
packageEnvironment: package.pubspec.environment,
);

final updatedDependenciesCount = _updateDependencies(
pubspecEditor: pubspecEditor,
workspaceDependencies: dependencies,
packageDependencies: package.pubSpec.dependencies,
packageDependencies: package.pubspec.dependencies,
pubspecKey: 'dependencies',
);

final updatedDevDependenciesCount = _updateDependencies(
pubspecEditor: pubspecEditor,
workspaceDependencies: devDependencies,
packageDependencies: package.pubSpec.devDependencies,
packageDependencies: package.pubspec.devDependencies,
pubspecKey: 'dev_dependencies',
);

Expand Down Expand Up @@ -327,38 +328,34 @@ mixin _BootstrapMixin on _CleanMixin {
pubspecEditor.update(
['environment', 'sdk'],
wrapAsYamlNode(
workspaceEnvironment.sdkConstraint.toString(),
workspaceEnvironment.sdkConstraint,
collectionStyle: CollectionStyle.BLOCK,
),
);
didUpdate = true;
}

final workspaceUnParsedYaml = workspaceEnvironment.unParsedYaml;
final packageUnParsedYaml = packageEnvironment.unParsedYaml;
if (workspaceUnParsedYaml != null && packageUnParsedYaml != null) {
for (final entry in workspaceUnParsedYaml.entries) {
if (!packageUnParsedYaml.containsKey(entry.key)) continue;
if (packageUnParsedYaml[entry.key] == entry.value) continue;
for (final entry in workspaceEnvironment.entries) {
if (!packageEnvironment.containsKey(entry.key)) continue;
if (packageEnvironment[entry.key] == entry.value) continue;

pubspecEditor.update(
['environment', entry.key],
wrapAsYamlNode(
entry.value.toString(),
collectionStyle: CollectionStyle.BLOCK,
),
);
didUpdate = true;
}
pubspecEditor.update(
['environment', entry.key],
wrapAsYamlNode(
entry.value.toString(),
collectionStyle: CollectionStyle.BLOCK,
),
);
didUpdate = true;
}

return didUpdate;
}

int _updateDependencies({
required YamlEditor pubspecEditor,
required Map<String, DependencyReference>? workspaceDependencies,
required Map<String, DependencyReference> packageDependencies,
required Map<String, Dependency>? workspaceDependencies,
required Map<String, Dependency> packageDependencies,
required String pubspecKey,
}) {
if (workspaceDependencies == null) return 0;
Expand Down Expand Up @@ -462,7 +459,7 @@ final _managedDependencyOverridesRegex = RegExp(
/// [melosDependencyOverrides] must contain a mapping of workspace package names
/// to their paths relative to the package.
///
/// [pubspecOverridesContents] are the current contents of the package's
/// [pubspecOverridesContent] are the current contents of the package's
/// `pubspec_overrides.yaml` and may be `null` if the file does not exist.
///
/// Whitespace and comments in an existing `pubspec_overrides.yaml` file are
Expand All @@ -484,21 +481,18 @@ final _managedDependencyOverridesRegex = RegExp(
/// obsolete from `dependency_overrides` and the marker comment.
@visibleForTesting
String? mergeMelosPubspecOverrides(
Map<String, DependencyReference> melosDependencyOverrides,
String? pubspecOverridesContents,
Map<String, Dependency> melosDependencyOverrides,
String? pubspecOverridesContent,
) {
// ignore: parameter_assignments
pubspecOverridesContents ??= '';

final pubspecOverridesEditor = YamlEditor(pubspecOverridesContents);
final pubspecOverridesEditor = YamlEditor(pubspecOverridesContent ?? '');
final pubspecOverrides = pubspecOverridesEditor
.parseAt([], orElse: () => wrapAsYamlNode(null)).value as Object?;
final dependencyOverrides = pubspecOverrides is Map &&
pubspecOverrides['dependency_overrides'] is Map
? {...pubspecOverrides['dependency_overrides'] as Map<Object?, Object?>}
: null;

final dependencyOverrides = pubspecOverridesContent?.isEmpty ?? true
? null
: PubspecOverrides.parse(pubspecOverridesContent!).dependencyOverrides;
final currentManagedDependencyOverrides = _managedDependencyOverridesRegex
.firstMatch(pubspecOverridesContents)
.firstMatch(pubspecOverridesContent ?? '')
?.group(1)
?.split(',')
.toSet() ??
Expand All @@ -507,22 +501,21 @@ String? mergeMelosPubspecOverrides(

if (dependencyOverrides != null) {
for (final dependencyOverride in dependencyOverrides.entries.toList()) {
final packageName = dependencyOverride.key!;
final packageName = dependencyOverride.key;

if (currentManagedDependencyOverrides.contains(packageName)) {
// This dependency override is managed by melos and might need to be
// updated.

if (melosDependencyOverrides.containsKey(packageName)) {
// Update changed dependency override.
final currentRef =
DependencyReference.fromJson(dependencyOverride.value);
final currentRef = dependencyOverride.value;
final newRef = melosDependencyOverrides[packageName];
if (currentRef != newRef) {
pubspecOverridesEditor.update(
['dependency_overrides', packageName],
wrapAsYamlNode(
newRef!.toJson() as Object,
newRef!.toJson(),
collectionStyle: CollectionStyle.BLOCK,
),
);
Expand Down Expand Up @@ -554,8 +547,7 @@ String? mergeMelosPubspecOverrides(
{
'dependency_overrides': {
for (final dependencyOverride in melosDependencyOverrides.entries)
dependencyOverride.key:
dependencyOverride.value.toJson() as Object,
dependencyOverride.key: dependencyOverride.value.toJson(),
},
},
collectionStyle: CollectionStyle.BLOCK,
Expand All @@ -568,8 +560,7 @@ String? mergeMelosPubspecOverrides(
wrapAsYamlNode(
{
for (final dependencyOverride in melosDependencyOverrides.entries)
dependencyOverride.key:
dependencyOverride.value.toJson() as Object,
dependencyOverride.key: dependencyOverride.value.toJson(),
},
collectionStyle: CollectionStyle.BLOCK,
),
Expand All @@ -579,7 +570,7 @@ String? mergeMelosPubspecOverrides(
pubspecOverridesEditor.update(
['dependency_overrides', dependencyOverride.key],
wrapAsYamlNode(
dependencyOverride.value.toJson() as Object,
dependencyOverride.value.toJson(),
collectionStyle: CollectionStyle.BLOCK,
),
);
Expand Down
4 changes: 2 additions & 2 deletions packages/melos/lib/src/commands/exec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ mixin _ExecMixin on _Melos {
p.normalize('$exampleParentPackagePath/pubspec.yaml');

if (fileExists(exampleParentPubspecPath)) {
final exampleParentPackage = PubSpec.fromYamlString(
final exampleParentPackage = Pubspec.parse(
await readTextFileAsync(exampleParentPubspecPath),
);

environment[EnvironmentVariableKey.melosParentPackageName] =
exampleParentPackage.name!;
exampleParentPackage.name;
environment[EnvironmentVariableKey.melosParentPackageVersion] =
(exampleParentPackage.version ?? Version.none).toString();
environment[EnvironmentVariableKey.melosParentPackagePath] =
Expand Down
Loading
Loading