blob: e091ea27b6bbd1ee995855dbf68e2857482969ca [file] [log] [blame]
// Copyright (c) 2017, 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:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/pubspec/pubspec_validator.dart';
import 'package:analyzer/src/pubspec/pubspec_warning_code.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer/src/util/yaml.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
class DependencyValidator extends BasePubspecValidator {
DependencyValidator(ResourceProvider provider, Source source)
: super(provider, source);
/// Validate the value of the required `name` field.
void validate(ErrorReporter reporter, Map<dynamic, YamlNode> contents) {
Map<dynamic, YamlNode> declaredDependencies = _getDeclaredDependencies(
reporter, contents, PubspecField.DEPENDENCIES_FIELD);
Map<dynamic, YamlNode> declaredDevDependencies = _getDeclaredDependencies(
reporter, contents, PubspecField.DEV_DEPENDENCIES_FIELD);
bool isPublishablePackage = false;
var version = contents[PubspecField.VERSION_FIELD];
if (version != null) {
var publishTo = asString(contents[PubspecField.PUBLISH_TO_FIELD]);
if (publishTo != 'none') {
isPublishablePackage = true;
}
}
for (var dependency in declaredDependencies.entries) {
_validatePathEntries(reporter, dependency.value, isPublishablePackage);
}
for (var dependency in declaredDevDependencies.entries) {
var packageName = dependency.key as YamlNode;
if (declaredDependencies.containsKey(packageName)) {
reportErrorForNode(reporter, packageName,
PubspecWarningCode.UNNECESSARY_DEV_DEPENDENCY, [packageName.value]);
}
_validatePathEntries(reporter, dependency.value, false);
}
}
/// Return a map whose keys are the names of declared dependencies and whose
/// values are the specifications of those dependencies. The map is extracted
/// from the given [contents] using the given [key].
Map<dynamic, YamlNode> _getDeclaredDependencies(
ErrorReporter reporter, Map<dynamic, YamlNode> contents, String key) {
var field = contents[key];
if (field == null || (field is YamlScalar && field.value == null)) {
return <String, YamlNode>{};
} else if (field is YamlMap) {
return field.nodes;
}
reportErrorForNode(
reporter, field, PubspecWarningCode.DEPENDENCIES_FIELD_NOT_MAP, [key]);
return <String, YamlNode>{};
}
/// Validate that `path` entries reference valid paths.
///
/// Valid paths are directories that:
///
/// 1. exist,
/// 2. contain a pubspec.yaml file
///
/// If [checkForPathAndGitDeps] is true, `git` or `path` dependencies will
/// be marked invalid.
void _validatePathEntries(ErrorReporter reporter, YamlNode dependency,
bool checkForPathAndGitDeps) {
if (dependency is YamlMap) {
var pathEntry = asString(dependency[PubspecField.PATH_FIELD]);
if (pathEntry != null) {
YamlNode pathKey() => dependency.getKey(PubspecField.PATH_FIELD)!;
YamlNode pathValue() => dependency.valueAt(PubspecField.PATH_FIELD)!;
if (pathEntry.contains(r'\')) {
reportErrorForNode(reporter, pathValue(),
PubspecWarningCode.PATH_NOT_POSIX, [pathEntry]);
return;
}
var context = provider.pathContext;
var normalizedPath = context.joinAll(path.posix.split(pathEntry));
var packageRoot = context.dirname(source.fullName);
var dependencyPath = context.join(packageRoot, normalizedPath);
dependencyPath = context.absolute(dependencyPath);
dependencyPath = context.normalize(dependencyPath);
var packageFolder = provider.getFolder(dependencyPath);
if (!packageFolder.exists) {
reportErrorForNode(reporter, pathValue(),
PubspecWarningCode.PATH_DOES_NOT_EXIST, [pathEntry]);
} else {
if (!packageFolder.getChild(file_paths.pubspecYaml).exists) {
reportErrorForNode(reporter, pathValue(),
PubspecWarningCode.PATH_PUBSPEC_DOES_NOT_EXIST, [pathEntry]);
}
}
if (checkForPathAndGitDeps) {
reportErrorForNode(reporter, pathKey(),
PubspecWarningCode.INVALID_DEPENDENCY, [PubspecField.PATH_FIELD]);
}
}
var gitEntry = dependency[PubspecField.GIT_FIELD];
if (gitEntry != null && checkForPathAndGitDeps) {
reportErrorForNode(reporter, dependency.getKey(PubspecField.GIT_FIELD)!,
PubspecWarningCode.INVALID_DEPENDENCY, [PubspecField.GIT_FIELD]);
}
}
}
}