| // Copyright (c) 2012, 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 'dart:async'; | |
| import '../path.dart'; | |
| import '../utils.dart'; | |
| import '../validator.dart'; | |
| /// A validator that the name of a package is legal and matches the library name | |
| /// in the case of a single library. | |
| class NameValidator extends Validator { | |
| @override | |
| Future validate() { | |
| return Future.sync(() { | |
| _checkName(package.name); | |
| final libraries = _libraries(files); | |
| if (libraries.length == 1) { | |
| final libName = p.basenameWithoutExtension(libraries[0]); | |
| if (libName == package.name) return; | |
| warnings.add( | |
| 'The name of "${libraries[0]}", "$libName", should match ' | |
| 'the name of the package, "${package.name}".\n' | |
| 'This helps users know what library to import.', | |
| ); | |
| } | |
| }); | |
| } | |
| /// Returns a list of all libraries in the current package as paths relative | |
| /// to the package's root directory. | |
| List<String> _libraries(List<String> files) { | |
| final libDir = package.path('lib'); | |
| return filesBeneath('lib', recursive: true) | |
| .map((file) => p.relative(file, from: p.dirname(libDir))) | |
| .where( | |
| (file) => | |
| !p.split(file).contains('src') && p.extension(file) == '.dart', | |
| ) | |
| .toList(); | |
| } | |
| void _checkName(String name) { | |
| final description = 'Package name "$name"'; | |
| if (name == '') { | |
| errors.add('$description may not be empty.'); | |
| } else if (!RegExp(r'^[a-zA-Z0-9_]*$').hasMatch(name)) { | |
| errors.add( | |
| '$description may only contain letters, numbers, and ' | |
| 'underscores.\n' | |
| 'Using a valid Dart identifier makes the name usable in Dart code.', | |
| ); | |
| } else if (!RegExp(r'^[a-zA-Z_]').hasMatch(name)) { | |
| errors.add( | |
| '$description must begin with a letter or underscore.\n' | |
| 'Using a valid Dart identifier makes the name usable in Dart code.', | |
| ); | |
| } else if (reservedWords.contains(name.toLowerCase())) { | |
| errors.add( | |
| '$description may not be a reserved word in Dart.\n' | |
| 'Using a valid Dart identifier makes the name usable in Dart code.', | |
| ); | |
| } else if (RegExp(r'[A-Z]').hasMatch(name)) { | |
| warnings.add( | |
| '$description should be lower-case. Maybe use ' | |
| '"${_unCamelCase(name)}"?', | |
| ); | |
| } | |
| } | |
| String _unCamelCase(String source) { | |
| final builder = StringBuffer(); | |
| var lastMatchEnd = 0; | |
| for (var match in RegExp(r'[a-z]([A-Z])').allMatches(source)) { | |
| builder | |
| ..write(source.substring(lastMatchEnd, match.start + 1)) | |
| ..write('_') | |
| ..write(match.group(1)!.toLowerCase()); | |
| lastMatchEnd = match.end; | |
| } | |
| builder.write(source.substring(lastMatchEnd)); | |
| return builder.toString().toLowerCase(); | |
| } | |
| } |