blob: c7c674aea19c79a918e345b33a4ab80c972df508 [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart';
import '../command.dart';
import '../entrypoint.dart';
import '../io.dart';
import '../log.dart' as log;
import '../package.dart';
import '../pubspec.dart';
import '../solver.dart';
/// Handles the `remove` pub command. Removes dependencies from `pubspec.yaml`,
/// and performs an operation similar to `pub get`. Unlike `pub add`, this
/// command supports the removal of multiple dependencies.
class RemoveCommand extends PubCommand {
@override
String get name => 'remove';
@override
String get description => 'Removes a dependency from the current package.';
@override
String get argumentsDescription => '<package>';
@override
String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-remove';
@override
bool get isOffline => argResults['offline'];
bool get isDryRun => argResults['dry-run'];
RemoveCommand() {
argParser.addFlag('offline',
help: 'Use cached packages instead of accessing the network.');
argParser.addFlag('dry-run',
abbr: 'n',
negatable: false,
help: "Report what dependencies would change but don't change any.");
argParser.addFlag('precompile',
help: 'Precompile executables in immediate dependencies.');
argParser.addFlag(
'example',
help: 'Also update dependencies in `example/` (if it exists).',
hide: true,
);
argParser.addOption('directory',
abbr: 'C', help: 'Run this in the directory<dir>.', valueHelp: 'dir');
}
@override
Future<void> runProtected() async {
if (argResults.rest.isEmpty) {
usageException('Must specify a package to be removed.');
}
final packages = Set<String>.from(argResults.rest);
if (isDryRun) {
final rootPubspec = entrypoint.root.pubspec;
final newPubspec = _removePackagesFromPubspec(rootPubspec, packages);
final newRoot = Package.inMemory(newPubspec);
await Entrypoint.global(newRoot, entrypoint.lockFile, cache)
.acquireDependencies(SolveType.GET,
precompile: argResults['precompile'],
dryRun: true,
analytics: null);
} else {
/// Update the pubspec.
_writeRemovalToPubspec(packages);
/// Create a new [Entrypoint] since we have to reprocess the updated
/// pubspec file.
final updatedEntrypoint = Entrypoint(directory, cache);
await updatedEntrypoint.acquireDependencies(
SolveType.GET,
precompile: argResults['precompile'],
analytics: analytics,
);
var example = entrypoint.example;
if (argResults['example'] && example != null) {
await example.acquireDependencies(
SolveType.GET,
precompile: argResults['precompile'],
onlyReportSuccessOrFailure: true,
analytics: analytics,
);
}
}
}
Pubspec _removePackagesFromPubspec(Pubspec original, Set<String> packages) {
final originalDependencies = original.dependencies.values;
final originalDevDependencies = original.devDependencies.values;
final newDependencies = originalDependencies
.where((dependency) => !packages.contains(dependency.name));
final newDevDependencies = originalDevDependencies
.where((dependency) => !packages.contains(dependency.name));
return Pubspec(
original.name,
version: original.version,
sdkConstraints: original.sdkConstraints,
dependencies: newDependencies,
devDependencies: newDevDependencies,
dependencyOverrides: original.dependencyOverrides.values,
);
}
/// Writes the changes to the pubspec file
void _writeRemovalToPubspec(Set<String> packages) {
ArgumentError.checkNotNull(packages, 'packages');
final yamlEditor = YamlEditor(readTextFile(entrypoint.pubspecPath));
for (var package in packages) {
var found = false;
/// There may be packages where the dependency is declared both in
/// dependencies and dev_dependencies.
for (final dependencyKey in ['dependencies', 'dev_dependencies']) {
final dependenciesNode = yamlEditor
.parseAt([dependencyKey], orElse: () => YamlScalar.wrap(null));
if (dependenciesNode is YamlMap &&
dependenciesNode.containsKey(package)) {
if (dependenciesNode.length == 1) {
yamlEditor.remove([dependencyKey]);
} else {
yamlEditor.remove([dependencyKey, package]);
}
found = true;
}
}
if (!found) {
log.warning('Package "$package" was not found in pubspec.yaml!');
}
/// Windows line endings are already handled by [yamlEditor]
writeTextFile(entrypoint.pubspecPath, yamlEditor.toString());
}
}
}