| // Copyright 2014 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 'dart:isolate'; |
| import 'dart:typed_data'; |
| |
| import 'package:meta/meta.dart'; |
| import 'package:package_config/package_config.dart'; |
| |
| import '../base/common.dart'; |
| import '../base/file_system.dart'; |
| import '../base/logger.dart'; |
| import '../globals.dart' as globals; |
| |
| /// Whether to ignore [Isolate.packageConfigSync] and force the fallback |
| /// path in [currentPackageConfig]. |
| @visibleForTesting |
| bool debugIgnorePackageConfigSync = false; |
| |
| const String _fileScheme = 'file'; |
| |
| /// Loads the package configuration of the current isolate. |
| Future<PackageConfig> currentPackageConfig() async { |
| final Uri? packageConfigUri = debugIgnorePackageConfigSync ? null : Isolate.packageConfigSync; |
| if (packageConfigUri != null) { |
| return loadPackageConfigUri(packageConfigUri); |
| } |
| |
| final FileSystem fileSystem = globals.fs; |
| final Directory cwd = fileSystem.currentDirectory; |
| File? packageConfigFile = findPackageConfigFile(cwd); |
| |
| if (packageConfigFile == null) { |
| final Uri scriptUri = globals.platform.script; |
| if (scriptUri.scheme == _fileScheme) { |
| final File scriptFile = fileSystem.file(scriptUri); |
| packageConfigFile = findPackageConfigFile(scriptFile.parent); |
| } |
| } |
| |
| if (packageConfigFile == null) { |
| throwToolExit( |
| 'Failed to resolve package configuration.\n' |
| 'Isolate.packageConfigSync was null, and no .dart_tool/package_config.json ' |
| 'could be found in the current working directory (${cwd.path}) or ' |
| 'relative to the script (${globals.platform.script}).\n' |
| 'Did you run "flutter pub get"?', |
| ); |
| } |
| |
| return loadPackageConfigWithLogging(packageConfigFile, logger: globals.logger); |
| } |
| |
| /// Locates the `.dart_tool/package_config.json` relevant to [dir]. |
| /// |
| /// Searches [dir] and all parent directories. |
| /// |
| /// Returns `null` if no package_config.json was found. |
| // TODO(sigurdm): Only call this once per run - and read in from BuildInfo. |
| File? findPackageConfigFile(Directory dir) { |
| final FileSystem fileSystem = dir.fileSystem; |
| Directory candidateDir = fileSystem.directory(fileSystem.path.normalize(dir.absolute.path)); |
| |
| while (true) { |
| final File candidatePackageConfigFile = candidateDir |
| .childDirectory('.dart_tool') |
| .childFile('package_config.json'); |
| if (candidatePackageConfigFile.existsSync()) { |
| return candidatePackageConfigFile; |
| } |
| final Directory parentDir = candidateDir.parent; |
| if (fileSystem.path.equals(parentDir.path, candidateDir.path)) { |
| return null; |
| } |
| candidateDir = parentDir; |
| } |
| } |
| |
| /// Locates the `.dart_tool/package_config.json` relevant to [dir]. |
| /// |
| /// Like [findPackageConfigFile] but returns |
| /// `$dir/.dart_tool/package_config.json` if no package config could be found. |
| File findPackageConfigFileOrDefault(Directory dir) { |
| return findPackageConfigFile(dir) ?? |
| dir.childDirectory('.dart_tool').childFile('package_config.json'); |
| } |
| |
| /// Load the package configuration from [file] or throws a [ToolExit] |
| /// if the operation would fail. |
| /// |
| /// If [throwOnError] is false, in the event of an error an empty package |
| /// config is returned. |
| Future<PackageConfig> loadPackageConfigWithLogging( |
| File file, { |
| required Logger logger, |
| bool throwOnError = true, |
| }) async { |
| final FileSystem fileSystem = file.fileSystem; |
| var didError = false; |
| final PackageConfig result = await loadPackageConfigUri( |
| file.absolute.uri, |
| loader: (Uri uri) async { |
| final File configFile = fileSystem.file(uri); |
| if (!configFile.existsSync()) { |
| return null; |
| } |
| return Future<Uint8List>.value(configFile.readAsBytesSync()); |
| }, |
| onError: (dynamic error) { |
| if (!throwOnError) { |
| return; |
| } |
| logger.printTrace(error.toString()); |
| var message = '${file.path} does not exist.'; |
| final String pubspecPath = fileSystem.path.absolute( |
| fileSystem.path.dirname(file.path), |
| 'pubspec.yaml', |
| ); |
| if (fileSystem.isFileSync(pubspecPath)) { |
| message += '\nDid you run "flutter pub get" in this directory?'; |
| } else { |
| message += '\nDid you run this command from the same directory as your pubspec.yaml file?'; |
| } |
| logger.printError(message); |
| didError = true; |
| }, |
| ); |
| if (didError) { |
| throwToolExit(''); |
| } |
| return result; |
| } |
| |
| extension PackageConfigWorkspaceExtension on PackageConfig { |
| /// Converts a [fileUri] to a `package:` URI, finding the most specific |
| /// package whose [Package.packageUriRoot] is a prefix of [fileUri]. |
| /// |
| /// The default [PackageConfig.toPackageUri] may match an outer package first |
| /// when pub workspace member packages are located under the workspace root |
| /// package's `lib/` directory. |
| Uri? toPackageUriForWorkspace(Uri fileUri) { |
| if (fileUri.isScheme('package')) { |
| return fileUri; |
| } |
| final path = fileUri.toString(); |
| Package? bestMatch; |
| String? bestMatchRoot; |
| |
| for (final Package package in packages) { |
| final rootPath = package.packageUriRoot.toString(); |
| final rootPathWithSlash = rootPath.endsWith('/') ? rootPath : '$rootPath/'; |
| if (path.startsWith(rootPathWithSlash)) { |
| if (bestMatchRoot == null || rootPathWithSlash.length > bestMatchRoot.length) { |
| bestMatch = package; |
| bestMatchRoot = rootPathWithSlash; |
| } |
| } |
| } |
| |
| if (bestMatch == null || bestMatchRoot == null) { |
| return null; |
| } |
| |
| final String rest = path.substring(bestMatchRoot.length); |
| return Uri(scheme: 'package', path: '${bestMatch.name}/$rest'); |
| } |
| } |