blob: f87f067d2d85e54c8d772a1b17b636495e6dc502 [file] [log] [blame]
// Copyright (c) 2013, 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.
library barback.transformer.transformer;
import 'dart:async';
import '../asset/asset_id.dart';
import '../utils.dart';
import 'transform.dart';
/// A [Transformer] represents a processor that takes in one or more input
/// assets and uses them to generate one or more output assets.
///
/// Dart2js, a SASS->CSS processor, a CSS spriter, and a tool to concatenate
/// files are all examples of transformers. To define your own transformation
/// step, extend (or implement) this class.
///
/// If possible, transformers should implement [DeclaringTransformer] as well to
/// help barback optimize the package graph.
abstract class Transformer {
/// Override this to return a space-separated list of file extensions that are
/// allowed for the primary inputs to this transformer.
///
/// Each extension must begin with a leading `.`.
///
/// If you don't override [isPrimary] yourself, it defaults to allowing any
/// asset whose extension matches one of the ones returned by this. If you
/// don't override [isPrimary] *or* this, it allows all files.
String get allowedExtensions => null;
Transformer() {
if (allowedExtensions == null) return;
var invalidExtensions = allowedExtensions
.split(" ")
.where((extension) => !extension.startsWith("."))
.map((extension) => '"$extension"');
if (invalidExtensions.isEmpty) return;
throw new FormatException('Each extension in $this.allowedExtensions '
'must begin with a ".", but ${toSentence(invalidExtensions)} '
'${pluralize("doesn't", invalidExtensions.length, plural: "don't")}.');
}
/// Returns `true` if [id] can be a primary input for this transformer.
///
/// While a transformer can read from multiple input files, one must be the
/// "primary" input. This asset determines whether the transformation should
/// be run at all. If the primary input is removed, the transformer will no
/// longer be run.
///
/// A concrete example is dart2js. When you run dart2js, it will traverse
/// all of the imports in your Dart source files and use the contents of all
/// of those to generate the final JS. However you still run dart2js "on" a
/// single file: the entrypoint Dart file that has your `main()` method.
/// This entrypoint file would be the primary input.
///
/// If this is not overridden, defaults to allow any asset whose extension
/// matches one of the ones returned by [allowedExtensions]. If *that* is
/// not overridden, allows all assets.
///
/// This may return a `Future<bool>` or, if it's entirely synchronous, a
/// `bool`.
isPrimary(AssetId id) {
// Allow all files if [primaryExtensions] is not overridden.
if (allowedExtensions == null) return true;
for (var extension in allowedExtensions.split(" ")) {
if (id.path.endsWith(extension)) return true;
}
return false;
}
/// Run this transformer on the primary input specified by [transform].
///
/// The [transform] is used by the [Transformer] for two purposes (in
/// addition to accessing the primary input). It can call `getInput()` to
/// request additional input assets. It also calls `addOutput()` to provide
/// generated assets back to the system. Either can be called multiple times,
/// in any order.
///
/// In other words, a Transformer's job is to find all inputs for a
/// transform, starting at the primary input, then generate all output assets
/// and yield them back to the transform.
///
/// If this does asynchronous work, it should return a [Future] that completes
/// once it's finished.
apply(Transform transform);
String toString() => runtimeType.toString().replaceAll("Transformer", "");
}