blob: 36de8dc664bb995f2ed200a8014b59ab597085e8 [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 pub.barback;
import 'dart:async';
import 'package:barback/barback.dart';
import 'barback/load_transformers.dart';
import 'barback/pub_package_provider.dart';
import 'barback/rewrite_import_transformer.dart';
import 'barback/server.dart';
import 'barback/watch_sources.dart';
import 'utils.dart';
/// Creates a [BarbackServer] serving on [host] and [port].
///
/// This transforms and serves all library and asset files in all packages in
/// [graph]. It loads any transformer plugins defined in packages in [graph] and
/// re-runs them as necessary when any input files change.
Future<BarbackServer> createServer(String host, int port, PackageGraph graph) {
var provider = new PubPackageProvider(graph);
var barback = new Barback(provider);
return BarbackServer.bind(host, port, barback, graph.entrypoint.root.name)
.then((server) {
watchSources(graph, barback);
var completer = new Completer();
// If any errors get emitted either by barback or by the server, including
// non-programmatic barback errors, they should take down the whole program.
var subscriptions = [
server.barback.errors.listen((error) {
if (error is TransformerException) error = error.error;
if (!completer.isCompleted) completer.completeError(error);
}),
server.barback.results.listen((_) {}, onError: (error) {
if (!completer.isCompleted) completer.completeError(error);
}),
server.results.listen((_) {}, onError: (error) {
if (!completer.isCompleted) completer.completeError(error);
})
];
_loadTransformers(server, graph).then((_) {
if (!completer.isCompleted) completer.complete(server);
}).catchError((error) {
if (!completer.isCompleted) completer.completeError(error);
});
return completer.future.whenComplete(() {
for (var subscription in subscriptions) {
subscription.cancel();
}
});
});
}
/// Loads all transformers depended on by packages in [graph].
///
/// This uses [server] to serve the Dart files from which transformers are
/// loaded, then adds the transformers to `server.barback`.
Future _loadTransformers(BarbackServer server, PackageGraph graph) {
// Add a rewrite transformer for each package, so that we can resolve
// "package:" imports while loading transformers.
var rewrite = new RewriteImportTransformer();
for (var package in graph.packages.values) {
server.barback.updateTransformers(package.name, [[rewrite]]);
}
// A map from each transformer id to the set of packages that use it.
var idsToPackages = new Map<AssetId, Set<String>>();
for (var package in graph.packages.values) {
for (var id in unionAll(package.pubspec.transformers)) {
idsToPackages.putIfAbsent(id, () => new Set<String>()).add(package.name);
}
}
// TODO(nweiz): support transformers that (possibly transitively)
// depend on other transformers.
var transformersForId = new Map<AssetId, Set<Transformer>>();
return Future.wait(idsToPackages.keys.map((id) {
return loadTransformers(server, id).then((transformers) {
if (transformers.isEmpty) {
var path = id.path.replaceFirst('lib/', '');
// Ensure that packages are listed in a deterministic order.
var packages = idsToPackages[id].toList();
packages.sort();
throw new ApplicationException(
"No transformers were defined in package:${id.package}/$path,\n"
"required by ${packages.join(', ')}.");
}
transformersForId[id] = transformers;
});
})).then((_) {
for (var package in graph.packages.values) {
var phases = package.pubspec.transformers.map((phase) {
return unionAll(phase.map((id) => transformersForId[id]));
});
server.barback.updateTransformers(package.name, phases);
}
});
}