discoverPackageCommands static method
Discovers commands from packages in .dart_tool/package_config.json that ship
a metro_commands.json (like commands.json; scripts must live in bin/).
Implementation
static Future<List<NyCommand>> discoverPackageCommands({
Directory? projectDirectory,
void Function(String message)? onWarning,
}) async {
final void Function(String message) warn =
onWarning ?? MetroConsole.writeInYellow;
final Directory project = (projectDirectory ?? Directory.current).absolute;
final Uri projectUri = project.uri;
final File packageConfig = File.fromUri(
projectUri.resolve(packageConfigPath),
);
if (!await packageConfig.exists()) return [];
List<dynamic> packages;
try {
final dynamic decoded = jsonDecode(await packageConfig.readAsString());
packages = (decoded is Map && decoded['packages'] is List)
? decoded['packages']
: const [];
} catch (e) {
warn('[Metro] Could not read $packageConfigPath: $e');
return [];
}
final List<NyCommand> commands = [];
for (final dynamic package in packages) {
if (package is! Map) continue;
final dynamic name = package['name'];
final dynamic rootUri = package['rootUri'];
if (name is! String || rootUri is! String) continue;
// rootUri is relative to the package_config.json file itself and may be
// percent-encoded, so resolve it as a URI rather than joining strings.
Uri packageRoot;
try {
packageRoot = packageConfig.uri.resolveUri(Uri.parse(rootUri));
} on FormatException {
continue;
}
if (!packageRoot.path.endsWith('/')) {
packageRoot = packageRoot.replace(path: '${packageRoot.path}/');
}
// Skip the project itself - it registers its commands in commands.json.
if (packageRoot == projectUri) continue;
final File manifest = File.fromUri(
packageRoot.resolve(packageCommandsManifest),
);
if (!await manifest.exists()) continue;
try {
final dynamic decoded = jsonDecode(await manifest.readAsString());
if (decoded is! List) {
throw const FormatException('Expected a JSON array of commands');
}
final List<dynamic> configs = [];
for (final dynamic config in decoded) {
if (config is! Map ||
config['name'] is! String ||
config['script'] is! String) {
warn(
'[Metro] Ignoring an invalid command in $packageCommandsManifest of package "$name" - "name" and "script" are required',
);
continue;
}
if (!_isBinScriptName(config['script'])) {
warn(
'[Metro] Ignoring "${config['category'] ?? name}:${config['name']}" in $packageCommandsManifest of package "$name" - "script" must name a Dart file directly inside the package\'s bin/ folder, e.g. "my_command.dart"',
);
continue;
}
configs.add(config);
}
commands.addAll(
await discoverCommands(
configs,
scriptDirectory: packageRoot.resolve('bin/').toFilePath(),
package: name,
defaultCategory: name,
workingDirectory: project.path,
),
);
} catch (e) {
warn(
'[Metro] Could not load $packageCommandsManifest from package "$name": $e',
);
}
}
commands.sort((a, b) {
final int byPackage = (a.package ?? '').compareTo(b.package ?? '');
if (byPackage != 0) return byPackage;
return a.fullName.compareTo(b.fullName);
});
return commands;
}