blob: f8bddcf34ae2a38aba9561ec407dc891a67d926e [file] [log] [blame] [edit]
// Copyright (c) 2024, 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:io';
import 'package:hooks/hooks.dart';
import 'data_asset.dart';
/// Extension on [BuildOutputBuilder] to handle data asset directories and
/// files.
extension BuildOutputBuilderAddDataAssetsDirectories on BuildOutputBuilder {
/// Extension on [BuildOutputBuilder] to handle data asset directories and
/// files.
///
/// This extension provides a convenient way for build hooks to add
/// [DataAsset] dependencies from one or more directories or individual files.
///
/// If any specified path does not exist, a [FileSystemException] is thrown.
/// Any error during the directory listing is caught and rethrown with
/// additional context.
///
/// When recursive is set to true, the method will also add all subdirectories
/// and their files as dependencies.
Future<void> addDataAssetDirectories(
List<String> paths, {
required BuildInput input,
bool recursive = false,
}) async {
String assetName(Uri assetUri) => assetUri
.toFilePath(windows: false)
.substring(input.packageRoot.toFilePath().length);
for (final path in paths) {
final resolvedUri = input.packageRoot.resolve(path);
final directory = Directory.fromUri(resolvedUri);
final file = File.fromUri(resolvedUri);
if (await directory.exists()) {
try {
addDependency(directory.uri);
await for (final entity in directory.list(
recursive: recursive,
followLinks: false,
)) {
if (entity is File) {
assets.data.add(
DataAsset(
package: input.packageName,
name: assetName(entity.uri),
file: entity.uri,
),
);
}
addDependency(entity.uri);
}
} on FileSystemException catch (e) {
throw FileSystemException(
'Error reading directory "$path": ${e.message}',
directory.path,
e.osError,
);
}
} else if (await file.exists()) {
assets.data.add(
DataAsset(
package: input.packageName,
name: assetName(file.uri),
file: file.uri,
),
);
addDependency(file.uri);
} else {
throw FileSystemException(
'Path does not exist',
resolvedUri.toFilePath(windows: Platform.isWindows),
);
}
}
}
}
/// Extension to the [HookConfig] providing access to configuration specific
/// to data assets.
extension HookConfigDataConfig on HookConfig {
bool get buildDataAssets => buildAssetTypes.contains(DataAssetType.type);
}
/// Link output extension for data assets.
extension LinkInputDataAssets on LinkInputAssets {
// Returns the data assets that were sent to this linker.
//
// NOTE: If the linker implementation depends on the contents of the files of
// the data assets (e.g. by transforming them, merging with other files, etc)
// then the linker script has to add those files as dependencies via
// [LinkOutput.addDependency] to ensure the linker script will be re-run if
// the content of the files changes.
Iterable<DataAsset> get data =>
encodedAssets.where((e) => e.isDataAsset).map(DataAsset.fromEncoded);
}
/// Extension on [BuildOutputBuilder] to add [DataAsset]s.
extension BuildOutputAssetsBuilderData on BuildOutputAssetsBuilder {
/// Provides access to emitting data assets.
BuildOutputDataAssetsBuilder get data => BuildOutputDataAssetsBuilder._(this);
}
/// Extension on [BuildOutputBuilder] to add [DataAsset]s.
final class BuildOutputDataAssetsBuilder {
final BuildOutputAssetsBuilder _output;
BuildOutputDataAssetsBuilder._(this._output);
/// Adds the given [asset] to the hook output with [routing].
void add(DataAsset asset, {AssetRouting routing = const ToAppBundle()}) =>
_output.addEncodedAsset(asset.encode(), routing: routing);
/// Adds the given [assets] to the hook output with [routing].
void addAll(
Iterable<DataAsset> assets, {
AssetRouting routing = const ToAppBundle(),
}) {
for (final asset in assets) {
add(asset, routing: routing);
}
}
}
/// Extension on [LinkOutputBuilder] to add [DataAsset]s.
extension LinkOutputAssetsBuilderData on LinkOutputAssetsBuilder {
/// Provides access to emitting data assets.
LinkOutputDataAssetsBuilder get data => LinkOutputDataAssetsBuilder(this);
}
/// Extension on [LinkOutputBuilder] to add [DataAsset]s.
final class LinkOutputDataAssetsBuilder {
final LinkOutputAssetsBuilder _output;
LinkOutputDataAssetsBuilder(this._output);
/// Adds the given [asset] to the link hook output.
void add(DataAsset asset) => _output.addEncodedAsset(asset.encode());
/// Adds the given [assets] to the link hook output.
void addAll(Iterable<DataAsset> assets) => assets.forEach(add);
}
/// Provides access to [DataAsset]s from a build hook output.
extension BuildOutputDataAssets on BuildOutputAssets {
List<DataAsset> get data => encodedAssets
.where((asset) => asset.isDataAsset)
.map<DataAsset>(DataAsset.fromEncoded)
.toList();
}
/// Provides access to [DataAsset]s from a link hook output.
extension LinkOutputDataAssets on LinkOutputAssets {
List<DataAsset> get data => encodedAssets
.where((asset) => asset.isDataAsset)
.map<DataAsset>(DataAsset.fromEncoded)
.toList();
}