| // Copyright 2013 The Flutter Authors. 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:file/file.dart'; |
| import 'package:path/path.dart' as p; |
| import 'package:pubspec_parse/pubspec_parse.dart'; |
| |
| import 'core.dart'; |
| |
| export 'package:pubspec_parse/pubspec_parse.dart' show Pubspec; |
| export 'core.dart' show FlutterPlatform; |
| |
| /// A package in the repository. |
| // |
| // TODO(stuartmorgan): Add more package-related info here, such as an on-demand |
| // cache of the parsed pubspec. |
| class RepositoryPackage { |
| /// Creates a representation of the package at [directory]. |
| RepositoryPackage(this.directory); |
| |
| /// The location of the package. |
| final Directory directory; |
| |
| /// The path to the package. |
| String get path => directory.path; |
| |
| /// Returns the string to use when referring to the package in user-targeted |
| /// messages. |
| /// |
| /// Callers should not expect a specific format for this string, since |
| /// it uses heuristics to try to be precise without being overly verbose. If |
| /// an exact format (e.g., published name, or basename) is required, that |
| /// should be used instead. |
| String get displayName { |
| List<String> components = directory.fileSystem.path.split(directory.path); |
| // Remove everything up to the packages directory. |
| final int packagesIndex = components.indexOf('packages'); |
| if (packagesIndex != -1) { |
| components = components.sublist(packagesIndex + 1); |
| } |
| // For the common federated plugin pattern of `foo/foo_subpackage`, drop |
| // the first part since it's not useful. |
| if (components.length >= 2 && |
| components[1].startsWith('${components[0]}_')) { |
| components = components.sublist(1); |
| } |
| return p.posix.joinAll(components); |
| } |
| |
| /// The package's top-level pubspec.yaml. |
| File get pubspecFile => directory.childFile('pubspec.yaml'); |
| |
| /// The package's top-level README. |
| File get readmeFile => directory.childFile('README.md'); |
| |
| /// The package's top-level README. |
| File get changelogFile => directory.childFile('CHANGELOG.md'); |
| |
| /// The package's top-level README. |
| File get authorsFile => directory.childFile('AUTHORS'); |
| |
| /// The lib directory containing the package's code. |
| Directory get libDirectory => directory.childDirectory('lib'); |
| |
| /// The test directory containing the package's Dart tests. |
| Directory get testDirectory => directory.childDirectory('test'); |
| |
| /// Returns the directory containing support for [platform]. |
| Directory platformDirectory(FlutterPlatform platform) { |
| late final String directoryName; |
| switch (platform) { |
| case FlutterPlatform.android: |
| directoryName = 'android'; |
| break; |
| case FlutterPlatform.ios: |
| directoryName = 'ios'; |
| break; |
| case FlutterPlatform.linux: |
| directoryName = 'linux'; |
| break; |
| case FlutterPlatform.macos: |
| directoryName = 'macos'; |
| break; |
| case FlutterPlatform.web: |
| directoryName = 'web'; |
| break; |
| case FlutterPlatform.windows: |
| directoryName = 'windows'; |
| break; |
| } |
| return directory.childDirectory(directoryName); |
| } |
| |
| /// Returns true if the package is an app that supports [platform]. |
| /// |
| /// The "app" prefix on this method is because this currently only works |
| /// for app packages (e.g., examples). |
| // TODO(stuartmorgan): Add support for non-app packages, by parsing the |
| // pubspec for `flutter:platform:` or `platform:` sections. |
| bool appSupportsPlatform(FlutterPlatform platform) { |
| return platformDirectory(platform).existsSync(); |
| } |
| |
| late final Pubspec _parsedPubspec = |
| Pubspec.parse(pubspecFile.readAsStringSync()); |
| |
| /// Returns the parsed [pubspecFile]. |
| /// |
| /// Caches for future use. |
| Pubspec parsePubspec() => _parsedPubspec; |
| |
| /// Returns true if the package depends on Flutter. |
| bool requiresFlutter() { |
| final Pubspec pubspec = parsePubspec(); |
| return pubspec.dependencies.containsKey('flutter'); |
| } |
| |
| /// True if this appears to be a federated plugin package, according to |
| /// repository conventions. |
| bool get isFederated => |
| directory.parent.basename != 'packages' && |
| directory.basename.startsWith(directory.parent.basename); |
| |
| /// True if this appears to be the app-facing package of a federated plugin, |
| /// according to repository conventions. |
| bool get isAppFacing => |
| directory.parent.basename != 'packages' && |
| directory.basename == directory.parent.basename; |
| |
| /// True if this appears to be a platform interface package, according to |
| /// repository conventions. |
| bool get isPlatformInterface => |
| directory.basename.endsWith('_platform_interface'); |
| |
| /// True if this appears to be a platform implementation package, according to |
| /// repository conventions. |
| bool get isPlatformImplementation => |
| // Any part of a federated plugin that isn't the platform interface and |
| // isn't the app-facing package should be an implementation package. |
| isFederated && |
| !isPlatformInterface && |
| directory.basename != directory.parent.basename; |
| |
| /// Returns the Flutter example packages contained in the package, if any. |
| Iterable<RepositoryPackage> getExamples() { |
| final Directory exampleDirectory = directory.childDirectory('example'); |
| if (!exampleDirectory.existsSync()) { |
| return <RepositoryPackage>[]; |
| } |
| if (isPackage(exampleDirectory)) { |
| return <RepositoryPackage>[RepositoryPackage(exampleDirectory)]; |
| } |
| // Only look at the subdirectories of the example directory if the example |
| // directory itself is not a Dart package, and only look one level below the |
| // example directory for other Dart packages. |
| return exampleDirectory |
| .listSync() |
| .where((FileSystemEntity entity) => isPackage(entity)) |
| // isPackage guarantees that the cast to Directory is safe. |
| .map((FileSystemEntity entity) => |
| RepositoryPackage(entity as Directory)); |
| } |
| |
| /// Returns the package that this package is a part of, if any. |
| /// |
| /// Currently this is limited to checking up two directories, since that |
| /// covers all the example structures currently used. |
| RepositoryPackage? getEnclosingPackage() { |
| final Directory parent = directory.parent; |
| if (isPackage(parent)) { |
| return RepositoryPackage(parent); |
| } |
| if (isPackage(parent.parent)) { |
| return RepositoryPackage(parent.parent); |
| } |
| return null; |
| } |
| } |