blob: f36f6e384c620a390dc3e05f8c0817fb77970c1c [file] [log] [blame]
// Copyright (c) 2018, 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 'package:build/build.dart';
/// Overridable behavior for a [Builder.build] method.
typedef BuildBehavior = FutureOr Function(
BuildStep buildStep, Map<String, List<String>> buildExtensions);
/// Copy the input asset to all possible output assets.
void _defaultBehavior(
BuildStep buildStep, Map<String, List<String>> buildExtensions) =>
_copyToAll(buildStep, buildExtensions);
T _identity<T>(T value) => value;
Future<String> _readAsset(BuildStep buildStep, AssetId assetId) =>
buildStep.readAsString(assetId);
/// Pass the input assetId through [readFrom] and duplicate the results of
/// [read] on that asset into every matching output based on [buildExtensions].
void _copyToAll(BuildStep buildStep, Map<String, List<String>> buildExtensions,
{AssetId Function(AssetId assetId) readFrom = _identity,
Future<String> Function(BuildStep buildStep, AssetId assetId) read =
_readAsset}) {
if (!buildExtensions.keys.any((e) => buildStep.inputId.path.endsWith(e))) {
throw ArgumentError('Only expected inputs with extension in '
'${buildExtensions.keys.toList()} but got ${buildStep.inputId}');
}
for (final inputExtension in buildExtensions.keys) {
if (!buildStep.inputId.path.endsWith(inputExtension)) continue;
for (final outputExtension in buildExtensions[inputExtension]) {
final newPath = _replaceSuffix(
buildStep.inputId.path, inputExtension, outputExtension);
final id = AssetId(buildStep.inputId.package, newPath);
buildStep.writeAsString(id, read(buildStep, readFrom(buildStep.inputId)));
}
}
}
/// A build behavior which reads [assetId] and copies it's content into every
/// output.
BuildBehavior copyFrom(AssetId assetId) => (buildStep, buildExtensions) =>
_copyToAll(buildStep, buildExtensions, readFrom: (_) => assetId);
/// A build behavior which writes either 'true' or 'false' depending on whether
/// [assetId] can be read.
BuildBehavior writeCanRead(AssetId assetId) =>
(BuildStep buildStep, Map<String, List<String>> buildExtensions) =>
_copyToAll(buildStep, buildExtensions,
readFrom: (_) => assetId,
read: (buildStep, assetId) async =>
'${await buildStep.canRead(assetId)}');
/// A [Builder.buildExtensions] which operats on assets ending in [from] and
/// creates outputs with [postFix] appended as the extension.
///
/// If [numCopies] is greater than 1 the postFix will also get a `.0`, `.1`...
Map<String, List<String>> appendExtension(String postFix,
{String from = '', int numCopies = 1}) =>
{
from: numCopies == 1
? ['$from$postFix']
: List.generate(numCopies, (i) => '$from$postFix.$i')
};
Map<String, List<String>> replaceExtension(String from, String to) => {
from: [to]
};
/// A [Builder] whose [build] method can be replaced with a closure.
class TestBuilder implements Builder {
@override
final Map<String, List<String>> buildExtensions;
final BuildBehavior _build;
final BuildBehavior _extraWork;
/// A stream of all the [BuildStep.inputId]s that are seen.
///
/// Events are added at the start of the [build] method.
final _buildInputsController = StreamController<AssetId>.broadcast();
Stream<AssetId> get buildInputs => _buildInputsController.stream;
/// A stream of all the [BuildStep.inputId]s that are completed.
///
/// Events are added at the end of the [build] method.
final _buildsCompletedController = StreamController<AssetId>.broadcast();
Stream<AssetId> get buildsCompleted => _buildsCompletedController.stream;
TestBuilder({
Map<String, List<String>> buildExtensions,
BuildBehavior build,
BuildBehavior extraWork,
}) : buildExtensions = buildExtensions ?? appendExtension('.copy'),
_build = build ?? _defaultBehavior,
_extraWork = extraWork;
@override
Future build(BuildStep buildStep) async {
if (!await buildStep.canRead(buildStep.inputId)) return;
_buildInputsController.add(buildStep.inputId);
await _build(buildStep, buildExtensions);
if (_extraWork != null) await _extraWork(buildStep, buildExtensions);
_buildsCompletedController.add(buildStep.inputId);
}
}
String _replaceSuffix(String path, String old, String replacement) =>
path.substring(0, path.length - old.length) + replacement;