From 3b525b483cbdeb3240fda46e83472b0116793c07 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" <nweiz@google.com> Date: Thu, 12 Sep 2013 19:40:21 +0000 Subject: [PATCH] Support passing configuration data to a transformer plugin. This also requires that transformer plugins define an [asPlugin] named constructor in order to be loaded as plugins. R=rnystrom@google.com Review URL: https://codereview.chromium.org//23522029 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge@27444 260f80e4-7a28-3924-810f-c04153c831b5 --- lib/src/barback.dart | 32 ++++++++ lib/src/barback/load_all_transformers.dart | 43 +++++----- lib/src/barback/load_transformers.dart | 78 +++++++++++++------ lib/src/pubspec.dart | 32 ++++++-- test/pubspec_test.dart | 9 +++ ...figuration_defaults_to_empty_map_test.dart | 59 ++++++++++++++ ...with_reserved_transformer_config_test.dart | 36 +++++++++ ...ransformer_when_config_is_passed_test.dart | 35 +++++++++ ...s_configuration_to_a_transformer_test.dart | 61 +++++++++++++++ ...rints_a_transform_error_in_apply_test.dart | 2 +- ...ints_a_transform_interface_error_test.dart | 2 +- test/serve/utils.dart | 4 +- ...tiates_configurable_transformers_test.dart | 76 ++++++++++++++++++ 13 files changed, 415 insertions(+), 54 deletions(-) create mode 100644 test/serve/configuration_defaults_to_empty_map_test.dart create mode 100644 test/serve/fails_to_load_a_pubspec_with_reserved_transformer_config_test.dart create mode 100644 test/serve/fails_to_load_an_unconfigurable_transformer_when_config_is_passed_test.dart create mode 100644 test/serve/passes_configuration_to_a_transformer_test.dart create mode 100644 test/serve/with_configuration_only_instantiates_configurable_transformers_test.dart diff --git a/lib/src/barback.dart b/lib/src/barback.dart index 001b0739..3c14a16f 100644 --- a/lib/src/barback.dart +++ b/lib/src/barback.dart @@ -15,6 +15,38 @@ import 'barback/server.dart'; import 'barback/watch_sources.dart'; import 'utils.dart'; +/// An identifier for a transformer and the configuration that will be passed to +/// it. +/// +/// It's possible that [asset] defines multiple transformers. If so, +/// [configuration] will be passed to all of them. +class TransformerId { + /// The asset containing the transformer. + final AssetId asset; + + /// The configuration to pass to the transformer. + /// + /// This will be null if no configuration was provided. + final Map configuration; + + TransformerId(this.asset, this.configuration) { + if (configuration == null) return; + for (var reserved in ['include', 'exclude']) { + if (!configuration.containsKey(reserved)) continue; + throw new FormatException('Configuration for transformer ' + '${idToLibraryIdentifier(asset)} may not include reserved key ' + '"$reserved".'); + } + } + + // TODO(nweiz): support deep equality on [configuration] as well. + bool operator==(other) => other is TransformerId && + other.asset == asset && + other.configuration == configuration; + + int get hashCode => asset.hashCode ^ configuration.hashCode; +} + /// Creates a [BarbackServer] serving on [host] and [port]. /// /// This transforms and serves all library and asset files in all packages in diff --git a/lib/src/barback/load_all_transformers.dart b/lib/src/barback/load_all_transformers.dart index 2f9414b6..e954da83 100644 --- a/lib/src/barback/load_all_transformers.dart +++ b/lib/src/barback/load_all_transformers.dart @@ -86,7 +86,7 @@ Future loadAllTransformers(BarbackServer server, PackageGraph graph) { var transformers = [[rewrite]]; return Future.forEach(graph.packages[package].pubspec.transformers, (phase) { - return Future.wait(phase.where((id) => id.package == package) + return Future.wait(phase.where((id) => id.asset.package == package) .map(loader.load)).then((_) { transformers.add(unionAll(phase.map( (id) => loader.transformersFor(id)))); @@ -148,7 +148,7 @@ Map<String, Set<String>> _computeOrderingDeps(PackageGraph graph) { /// Returns the set of transformer dependencies for [package]. Set<String> _transformerDeps(PackageGraph graph, String package) => unionAll(graph.packages[package].pubspec.transformers) - .map((id) => id.package).toSet(); + .map((id) => id.asset.package).toSet(); /// Returns an [ApplicationException] describing an ordering dependency cycle /// detected in [graph]. @@ -167,7 +167,7 @@ ApplicationException _cycleError(PackageGraph graph, String dependee, return new ApplicationException("Transformer cycle detected:\n" + pairs(path).map((pair) { var transformers = unionAll(graph.packages[pair.first].pubspec.transformers) - .where((id) => id.package == pair.last) + .where((id) => id.asset.package == pair.last) .map(idToLibraryIdentifier).toList(); if (transformers.isEmpty) { return " ${pair.first} depends on ${pair.last}"; @@ -177,15 +177,16 @@ ApplicationException _cycleError(PackageGraph graph, String dependee, }).join("\n")); } -/// Returns a map from each package name in [graph] to the asset ids of all -/// transformers exposed by that package and used by other packages. -Map<String, Set<AssetId>> _computePackageTransformers(PackageGraph graph) { +/// Returns a map from each package name in [graph] to the transformer ids of +/// all transformers exposed by that package and used by other packages. +Map<String, Set<TransformerId>> _computePackageTransformers( + PackageGraph graph) { var packageTransformers = listToMap(graph.packages.values, - (package) => package.name, (_) => new Set<AssetId>()); + (package) => package.name, (_) => new Set<TransformerId>()); for (var package in graph.packages.values) { for (var phase in package.pubspec.transformers) { for (var id in phase) { - packageTransformers[id.package].add(id); + packageTransformers[id.asset.package].add(id); } } } @@ -196,11 +197,11 @@ Map<String, Set<AssetId>> _computePackageTransformers(PackageGraph graph) { class _TransformerLoader { final BarbackServer _server; - /// The loaded transformers defined in the library identified by each asset - /// id. - final _transformers = new Map<AssetId, Set<Transformer>>(); + /// The loaded transformers defined in the library identified by each + /// transformer id. + final _transformers = new Map<TransformerId, Set<Transformer>>(); - /// The packages that use each transformer id. + /// The packages that use each transformer asset id. /// /// Used for error reporting. final _transformerUsers = new Map<AssetId, Set<String>>(); @@ -208,29 +209,35 @@ class _TransformerLoader { _TransformerLoader(this._server, PackageGraph graph) { for (var package in graph.packages.values) { for (var id in unionAll(package.pubspec.transformers)) { - _transformerUsers.putIfAbsent(id, () => new Set<String>()) + _transformerUsers.putIfAbsent(id.asset, () => new Set<String>()) .add(package.name); } } } - /// Loads the transformer(s) defined in the asset [id]. + /// Loads the transformer(s) defined in [id]. /// /// Once the returned future completes, these transformers can be retrieved /// using [transformersFor]. If [id] doesn't define any transformers, this /// will complete to an error. - Future load(AssetId id) { + Future load(TransformerId id) { if (_transformers.containsKey(id)) return new Future.value(); + // TODO(nweiz): load multiple instances of the same transformer from the + // same isolate rather than spinning up a separate isolate for each one. return loadTransformers(_server, id).then((transformers) { if (!transformers.isEmpty) { _transformers[id] = transformers; return; } + var message = "No transformers"; + if (id.configuration != null) { + message += " that accept configuration"; + } throw new ApplicationException( - "No transformers were defined in ${idToPackageUri(id)},\n" - "required by ${ordered(_transformerUsers[id]).join(', ')}."); + "$message were defined in ${idToPackageUri(id.asset)},\n" + "required by ${ordered(_transformerUsers[id.asset]).join(', ')}."); }); } @@ -238,7 +245,7 @@ class _TransformerLoader { /// /// It's an error to call this before [load] is called with [id] and the /// future it returns has completed. - Set<Transformers> transformersFor(AssetId id) { + Set<Transformers> transformersFor(TransformerId id) { assert(_transformers.containsKey(id)); return _transformers[id]; } diff --git a/lib/src/barback/load_transformers.dart b/lib/src/barback/load_transformers.dart index 4d736c8f..b2c8464d 100644 --- a/lib/src/barback/load_transformers.dart +++ b/lib/src/barback/load_transformers.dart @@ -5,6 +5,7 @@ library pub.load_transformers; import 'dart:async'; +import 'dart:convert'; import 'dart:isolate'; import 'package:barback/barback.dart'; @@ -21,15 +22,19 @@ import '../utils.dart'; const _TRANSFORMER_ISOLATE = """ import 'dart:async'; import 'dart:isolate'; +import 'dart:convert'; import 'dart:mirrors'; import 'http://localhost:<<PORT>>/packages/barback/barback.dart'; /// Sets up the initial communication with the host isolate. void main() { - port.receive((uri, replyTo) { + port.receive((args, replyTo) { _sendFuture(replyTo, new Future.sync(() { - return initialize(Uri.parse(uri)).map(_serializeTransformer).toList(); + var library = Uri.parse(args['library']); + var configuration = JSON.decode(args['configuration']); + return initialize(library, configuration). + map(_serializeTransformer).toList(); })); }); } @@ -37,8 +42,8 @@ void main() { /// Loads all the transformers defined in [uri] and adds them to [transformers]. /// /// We then load the library, find any Transformer subclasses in it, instantiate -/// them, and return them. -Iterable<Transformer> initialize(Uri uri) { +/// them (with [configuration] if it's non-null), and return them. +Iterable<Transformer> initialize(Uri uri, Map configuration) { var mirrors = currentMirrorSystem(); // TODO(nweiz): look this up by name once issue 5897 is fixed. var transformerUri = Uri.parse( @@ -46,17 +51,30 @@ Iterable<Transformer> initialize(Uri uri) { var transformerClass = mirrors.libraries[transformerUri] .classes[const Symbol('Transformer')]; - return mirrors.libraries[uri].classes.values.where((classMirror) { - if (classMirror.isPrivate) return false; - if (isAbstract(classMirror)) return false; - if (!classIsA(classMirror, transformerClass)) return false; - var constructor = classMirror.constructors[classMirror.simpleName]; - if (constructor == null) return false; - if (!constructor.parameters.isEmpty) return false; - return true; - }).map((classMirror) { - return classMirror.newInstance(const Symbol(''), []).reflectee; - }); + // TODO(nweiz): if no valid transformers are found, throw an error message + // describing candidates and why they were rejected. + return mirrors.libraries[uri].classes.values.map((classMirror) { + if (classMirror.isPrivate) return null; + if (isAbstract(classMirror)) return null; + if (!classIsA(classMirror, transformerClass)) return null; + + var constructor = getConstructor(classMirror, 'asPlugin'); + if (constructor == null) return null; + if (constructor.parameters.isEmpty) { + if (configuration != null) return null; + return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee; + } + if (constructor.parameters.length != 1) return null; + + // If the constructor expects configuration and none was passed, it defaults + // to an empty map. + if (configuration == null) configuration = {}; + + // TODO(nweiz): if the constructor accepts named parameters, automatically + // destructure the configuration map. + return classMirror.newInstance(const Symbol('asPlugin'), [configuration]) + .reflectee; + }).where((classMirror) => classMirror != null); } /// A wrapper for a [Transform] that's in the host isolate. @@ -107,6 +125,13 @@ ClassMirror get objectMirror { } ClassMirror _objectMirror; +// TODO(nweiz): clean this up when issue 13248 is fixed. +MethodMirror getConstructor(ClassMirror classMirror, String constructor) { + var name = new Symbol("\${MirrorSystem.getName(classMirror.simpleName)}" + ".\$constructor"); + return classMirror.constructors[name]; +} + // TODO(nweiz): get rid of this when issue 12439 is fixed. /// Returns whether or not [mirror] is a subtype of [superclass]. /// @@ -256,23 +281,28 @@ String getErrorMessage(error) { } """; -/// Load and return all transformers from the library identified by [library]. +/// Load and return all transformers from the library identified by [id]. /// -/// [server] is used to serve [library] and any Dart files it imports. +/// [server] is used to serve any Dart files needed to load the transformer. Future<Set<Transformer>> loadTransformers(BarbackServer server, - AssetId library) { - var path = library.path.replaceFirst('lib/', ''); + TransformerId id) { + var path = id.asset.path.replaceFirst('lib/', ''); // TODO(nweiz): load from a "package:" URI when issue 12474 is fixed. - var uri = 'http://localhost:${server.port}/packages/${library.package}/$path'; + var uri = 'http://localhost:${server.port}/packages/${id.asset.package}/' + '$path'; var code = 'import "$uri";' + _TRANSFORMER_ISOLATE.replaceAll('<<PORT>>', server.port.toString()); - log.fine("Loading transformers from $library"); + log.fine("Loading transformers from ${id.asset}"); return dart.runInIsolate(code).then((sendPort) { - return _receiveFuture(sendPort.call(uri)).then((transformers) { + return _receiveFuture(sendPort.call({ + 'library': uri, + // TODO(nweiz): support non-JSON-encodable configuration maps. + 'configuration': JSON.encode(id.configuration) + })).then((transformers) { transformers = transformers .map((transformer) => new _ForeignTransformer(transformer)) .toSet(); - log.fine("Transformers from $library: $transformers"); + log.fine("Transformers from ${id.asset}: $transformers"); return transformers; }); }).catchError((error) { @@ -287,7 +317,7 @@ Future<Set<Transformer>> loadTransformers(BarbackServer server, // If there was an IsolateSpawnException and the import that actually failed // was the one we were loading transformers from, throw an application // exception with a more user-friendly message. - fail('Transformer library "package:${library.package}/$path" not found.'); + fail('Transformer library "package:${id.asset.package}/$path" not found.'); }); } diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart index d5b162ed..0813315f 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart @@ -30,9 +30,8 @@ class Pubspec { /// The packages this package depends on when it is the root package. final List<PackageDep> devDependencies; - /// The ids of the libraries containing the transformers to use for this - /// package. - final List<Set<AssetId>> transformers; + /// The ids of the transformers to use for this package. + final List<Set<TransformerId>> transformers; /// The environment-related metadata. final PubspecEnvironment environment; @@ -74,7 +73,7 @@ class Pubspec { dependencies = <PackageDep>[], devDependencies = <PackageDep>[], environment = new PubspecEnvironment(), - transformers = <Set<AssetId>>[], + transformers = <Set<TransformerId>>[], fields = {}; /// Whether or not the pubspec has no contents. @@ -190,19 +189,36 @@ Pubspec _parseMap(String filePath, Map map, SourceRegistry sources) { transformers = transformers.map((phase) { if (phase is! List) phase = [phase]; return phase.map((transformer) { - if (transformer is! String) { + if (transformer is! String && transformer is! Map) { throw new FormatException( - 'Transformer "$transformer" must be a string.'); + 'Transformer "$transformer" must be a string or map.'); + } + + var id; + var configuration; + if (transformer is String) { + id = libraryIdentifierToId(transformer); + } else { + if (transformer.length != 1) { + throw new FormatException('Transformer map "$transformer" must ' + 'have a single key: the library identifier.'); + } + + id = libraryIdentifierToId(transformer.keys.single); + configuration = transformer.values.single; + if (configuration is! Map) { + throw new FormatException('Configuration for transformer "$id" ' + 'must be a map, but was "$configuration".'); + } } - var id = libraryIdentifierToId(transformer); if (id.package != name && !dependencies.any((ref) => ref.name == id.package)) { throw new FormatException('Could not find package for transformer ' '"$transformer".'); } - return id; + return new TransformerId(id, configuration); }).toSet(); }).toList(); } else { diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart index 092611d3..44044619 100644 --- a/test/pubspec_test.dart +++ b/test/pubspec_test.dart @@ -214,6 +214,15 @@ version: not version expectFormatError('{author: abe, authors: ted}'); }); + test("throws if a transformer isn't a string or map", () { + expectFormatError('{transformers: 12}'); + expectFormatError('{transformers: [12]}'); + }); + + test("throws if a transformer's configuration isn't a map", () { + expectFormatError('{transformers: {pkg: 12}}'); + }); + test("allows comment-only files", () { var pubspec = new Pubspec.parse(null, ''' # No external dependencies yet diff --git a/test/serve/configuration_defaults_to_empty_map_test.dart b/test/serve/configuration_defaults_to_empty_map_test.dart new file mode 100644 index 00000000..bad8e520 --- /dev/null +++ b/test/serve/configuration_defaults_to_empty_map_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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_tests; + +import 'dart:convert'; + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; +import 'utils.dart'; + +final transformer = """ +import 'dart:async'; +import 'dart:convert'; + +import 'package:barback/barback.dart'; + +class ConfigTransformer extends Transformer { + final Map configuration; + + ConfigTransformer.asPlugin(this.configuration); + + String get allowedExtensions => '.txt'; + + Future apply(Transform transform) { + return transform.primaryInput.readAsString().then((contents) { + var id = transform.primaryInput.id.changeExtension(".json"); + transform.addOutput(new Asset.fromString(id, JSON.encode(configuration))); + }); + } +} +"""; + +main() { + initConfig(); + integration("configuration defaults to an empty map", () { + d.dir(appPath, [ + d.pubspec({ + "name": "myapp", + "transformers": ["myapp/src/transformer"] + }), + d.dir("lib", [d.dir("src", [ + d.file("transformer.dart", transformer) + ])]), + d.dir("web", [ + d.file("foo.txt", "foo") + ]) + ]).create(); + + createLockFile('myapp', pkg: ['barback']); + + var server = startPubServe(); + requestShouldSucceed("foo.json", JSON.encode({})); + endPubServe(); + }); +} diff --git a/test/serve/fails_to_load_a_pubspec_with_reserved_transformer_config_test.dart b/test/serve/fails_to_load_a_pubspec_with_reserved_transformer_config_test.dart new file mode 100644 index 00000000..5ba03d83 --- /dev/null +++ b/test/serve/fails_to_load_a_pubspec_with_reserved_transformer_config_test.dart @@ -0,0 +1,36 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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_tests; + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; +import 'utils.dart'; + +main() { + initConfig(); + + integration("fails to load a pubspec with reserved transformer config", () { + d.dir(appPath, [ + d.pubspec({ + "name": "myapp", + "transformers": [{"myapp/src/transformer": {'include': 'something'}}] + }), + d.dir("lib", [d.dir("src", [ + d.file("transformer.dart", REWRITE_TRANSFORMER) + ])]) + ]).create(); + + createLockFile('myapp', pkg: ['barback']); + + var pub = startPub(args: ['serve', '--port=0']); + expect(pub.nextErrLine(), completion(startsWith('Could not parse '))); + expect(pub.nextErrLine(), completion(equals('Configuration for ' + 'transformer myapp/src/transformer may not include reserved key ' + '"include".'))); + pub.shouldExit(1); + }); +} diff --git a/test/serve/fails_to_load_an_unconfigurable_transformer_when_config_is_passed_test.dart b/test/serve/fails_to_load_an_unconfigurable_transformer_when_config_is_passed_test.dart new file mode 100644 index 00000000..04036416 --- /dev/null +++ b/test/serve/fails_to_load_an_unconfigurable_transformer_when_config_is_passed_test.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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_tests; + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; +import 'utils.dart'; + +main() { + initConfig(); + + integration("fails to load an unconfigurable transformer when config is " + "passed", () { + d.dir(appPath, [ + d.pubspec({ + "name": "myapp", + "transformers": [{"myapp/src/transformer": {'foo': 'bar'}}] + }), + d.dir("lib", [d.dir("src", [ + d.file("transformer.dart", REWRITE_TRANSFORMER) + ])]) + ]).create(); + + createLockFile('myapp', pkg: ['barback']); + + var pub = startPub(args: ['serve', '--port=0']); + expect(pub.nextErrLine(), completion(startsWith('No transformers that ' + 'accept configuration were defined in '))); + pub.shouldExit(1); + }); +} diff --git a/test/serve/passes_configuration_to_a_transformer_test.dart b/test/serve/passes_configuration_to_a_transformer_test.dart new file mode 100644 index 00000000..e3f68e10 --- /dev/null +++ b/test/serve/passes_configuration_to_a_transformer_test.dart @@ -0,0 +1,61 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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_tests; + +import 'dart:convert'; + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; +import 'utils.dart'; + +final transformer = """ +import 'dart:async'; +import 'dart:convert'; + +import 'package:barback/barback.dart'; + +class ConfigTransformer extends Transformer { + final Map configuration; + + ConfigTransformer.asPlugin(this.configuration); + + String get allowedExtensions => '.txt'; + + Future apply(Transform transform) { + return transform.primaryInput.readAsString().then((contents) { + var id = transform.primaryInput.id.changeExtension(".json"); + transform.addOutput(new Asset.fromString(id, JSON.encode(configuration))); + }); + } +} +"""; + +main() { + initConfig(); + integration("passes configuration to a transformer", () { + var configuration = {"param": ["list", "of", "values"]}; + + d.dir(appPath, [ + d.pubspec({ + "name": "myapp", + "transformers": [{"myapp/src/transformer": configuration}] + }), + d.dir("lib", [d.dir("src", [ + d.file("transformer.dart", transformer) + ])]), + d.dir("web", [ + d.file("foo.txt", "foo") + ]) + ]).create(); + + createLockFile('myapp', pkg: ['barback']); + + var server = startPubServe(); + requestShouldSucceed("foo.json", JSON.encode(configuration)); + endPubServe(); + }); +} diff --git a/test/serve/prints_a_transform_error_in_apply_test.dart b/test/serve/prints_a_transform_error_in_apply_test.dart index 93b24a90..2d90ee84 100644 --- a/test/serve/prints_a_transform_error_in_apply_test.dart +++ b/test/serve/prints_a_transform_error_in_apply_test.dart @@ -16,7 +16,7 @@ import 'dart:async'; import 'package:barback/barback.dart'; class RewriteTransformer extends Transformer { - RewriteTransformer(); + RewriteTransformer.asPlugin(); String get allowedExtensions => '.txt'; diff --git a/test/serve/prints_a_transform_interface_error_test.dart b/test/serve/prints_a_transform_interface_error_test.dart index 47636b48..9e35a461 100644 --- a/test/serve/prints_a_transform_interface_error_test.dart +++ b/test/serve/prints_a_transform_interface_error_test.dart @@ -16,7 +16,7 @@ import 'dart:async'; import 'package:barback/barback.dart'; class RewriteTransformer extends Transformer { - RewriteTransformer(); + RewriteTransformer.asPlugin(); String get allowedExtensions => '.txt'; } diff --git a/test/serve/utils.dart b/test/serve/utils.dart index 7025d9b9..46ace9de 100644 --- a/test/serve/utils.dart +++ b/test/serve/utils.dart @@ -26,7 +26,7 @@ import 'dart:async'; import 'package:barback/barback.dart'; class RewriteTransformer extends Transformer { - RewriteTransformer(); + RewriteTransformer.asPlugin(); String get allowedExtensions => '.txt'; @@ -69,7 +69,7 @@ const TOKEN = "$id"; final _tokenRegExp = new RegExp(r'^const TOKEN = "(.*?)";\$', multiLine: true); class DartTransformer extends Transformer { - DartTransformer(); + DartTransformer.asPlugin(); String get allowedExtensions => '.dart'; diff --git a/test/serve/with_configuration_only_instantiates_configurable_transformers_test.dart b/test/serve/with_configuration_only_instantiates_configurable_transformers_test.dart new file mode 100644 index 00000000..6c010bf5 --- /dev/null +++ b/test/serve/with_configuration_only_instantiates_configurable_transformers_test.dart @@ -0,0 +1,76 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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_tests; + +import 'dart:convert'; + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; +import 'utils.dart'; + +final transformer = """ +import 'dart:async'; +import 'dart:convert'; + +import 'package:barback/barback.dart'; + +class ConfigTransformer extends Transformer { + final Map configuration; + + ConfigTransformer.asPlugin(this.configuration); + + String get allowedExtensions => '.txt'; + + Future apply(Transform transform) { + return transform.primaryInput.readAsString().then((contents) { + var id = transform.primaryInput.id.changeExtension(".json"); + transform.addOutput(new Asset.fromString(id, JSON.encode(configuration))); + }); + } +} + +class RewriteTransformer extends Transformer { + RewriteTransformer.asPlugin(); + + String get allowedExtensions => '.txt'; + + Future apply(Transform transform) { + return transform.primaryInput.readAsString().then((contents) { + var id = transform.primaryInput.id.changeExtension(".out"); + transform.addOutput(new Asset.fromString(id, "\$contents.out")); + }); + } +} +"""; + +main() { + initConfig(); + integration("with configuration, only instantiates configurable transformers", + () { + var configuration = {"param": ["list", "of", "values"]}; + + d.dir(appPath, [ + d.pubspec({ + "name": "myapp", + "transformers": [{"myapp/src/transformer": configuration}] + }), + d.dir("lib", [d.dir("src", [ + d.file("transformer.dart", transformer) + ])]), + d.dir("web", [ + d.file("foo.txt", "foo") + ]) + ]).create(); + + createLockFile('myapp', pkg: ['barback']); + + var server = startPubServe(); + requestShouldSucceed("foo.json", JSON.encode(configuration)); + requestShould404("foo.out"); + endPubServe(); + }); +} -- GitLab