├── example
├── simple_transformer
│ ├── lib
│ │ ├── test.txt
│ │ └── transformer.dart
│ ├── README.md
│ └── pubspec.yaml
├── markdown_converter
│ ├── lib
│ │ ├── test2.md
│ │ ├── images
│ │ │ └── bison.jpg
│ │ ├── index.markdown
│ │ └── transformer.dart
│ ├── README.md
│ └── pubspec.yaml
├── aggregate_transformer
│ ├── README.md
│ ├── pubspec.yaml
│ └── lib
│ │ ├── recipes-grammas
│ │ ├── winter-squash-pie-recipe.html
│ │ └── banana-pudding-recipe.html
│ │ └── transformer.dart
└── lazy_transformer
│ ├── pubspec.yaml
│ ├── lib
│ ├── message.txt
│ └── transformer.dart
│ └── README.md
├── .analysis_options
├── codereview.settings
├── .gitignore
├── test
├── transformer
│ ├── lazy_rewrite.dart
│ ├── lazy_assets.dart
│ ├── emit_nothing.dart
│ ├── create_asset.dart
│ ├── lazy_aggregate_many_to_many.dart
│ ├── lazy_aggregate_many_to_one.dart
│ ├── lazy_many_to_one.dart
│ ├── declaring_aggregate_many_to_many.dart
│ ├── lazy_check_content_and_rename.dart
│ ├── declaring_rewrite.dart
│ ├── declaring_aggregate_many_to_one.dart
│ ├── bad_log.dart
│ ├── check_content.dart
│ ├── conditionally_consume_primary.dart
│ ├── bad.dart
│ ├── declaring_check_content_and_rename.dart
│ ├── lazy_bad.dart
│ ├── has_input.dart
│ ├── sync_rewrite.dart
│ ├── one_to_many.dart
│ ├── aggregate_many_to_many.dart
│ ├── catch_asset_not_found.dart
│ ├── log.dart
│ ├── rewrite.dart
│ ├── check_content_and_rename.dart
│ ├── declaring_bad.dart
│ ├── declare_assets.dart
│ ├── many_to_one.dart
│ └── aggregate_many_to_one.dart
├── barback_mode_test.dart
├── package_graph
│ ├── many_parallel_transformers_test.dart
│ ├── repetition_test.dart
│ ├── get_all_assets_test.dart
│ ├── source_test.dart
│ └── transform
│ │ └── consume_input_test.dart
├── static_provider_test.dart
├── cancelable_future_test.dart
├── too_many_open_files_test.dart
├── logger_test.dart
├── transformer_test.dart
├── asset_id_test.dart
├── multiset_test.dart
├── stream_replayer_test.dart
└── asset_set_test.dart
├── README.md
├── lib
├── src
│ ├── transformer
│ │ ├── lazy_transformer.dart
│ │ ├── lazy_aggregate_transformer.dart
│ │ ├── declaring_transformer.dart
│ │ ├── transformer_group.dart
│ │ ├── declaring_aggregate_transformer.dart
│ │ ├── barback_settings.dart
│ │ ├── aggregate_transformer.dart
│ │ ├── declaring_transform.dart
│ │ ├── transform_logger.dart
│ │ ├── wrapping_aggregate_transformer.dart
│ │ ├── transformer.dart
│ │ ├── declaring_aggregate_transform.dart
│ │ ├── base_transform.dart
│ │ ├── transform.dart
│ │ └── aggregate_transform.dart
│ ├── internal_asset.dart
│ ├── log.dart
│ ├── asset
│ │ ├── asset_node_set.dart
│ │ ├── asset_forwarder.dart
│ │ ├── asset.dart
│ │ ├── asset_set.dart
│ │ ├── asset_id.dart
│ │ └── internal_asset.dart
│ ├── utils
│ │ ├── cancelable_future.dart
│ │ ├── multiset.dart
│ │ ├── file_pool.dart
│ │ ├── stream_replayer.dart
│ │ └── stream_pool.dart
│ ├── package_provider.dart
│ ├── graph
│ │ ├── node_status.dart
│ │ ├── node_streams.dart
│ │ ├── group_runner.dart
│ │ ├── static_asset_cascade.dart
│ │ ├── phase_output.dart
│ │ ├── phase_forwarder.dart
│ │ └── transformer_classifier.dart
│ ├── build_result.dart
│ ├── serialize.dart
│ └── barback.dart
└── barback.dart
├── .status
├── pubspec.yaml
└── LICENSE
/example/simple_transformer/lib/test.txt:
--------------------------------------------------------------------------------
1 | ABC
2 |
--------------------------------------------------------------------------------
/.analysis_options:
--------------------------------------------------------------------------------
1 | analyzer:
2 | strong-mode: true
3 |
--------------------------------------------------------------------------------
/example/markdown_converter/lib/test2.md:
--------------------------------------------------------------------------------
1 |
2 | # Another first level header
3 |
4 | * _Italics_
5 | * `literal`
6 | * **bold**
7 |
8 |
--------------------------------------------------------------------------------
/example/markdown_converter/lib/images/bison.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-archive/barback/master/example/markdown_converter/lib/images/bison.jpg
--------------------------------------------------------------------------------
/codereview.settings:
--------------------------------------------------------------------------------
1 | CODE_REVIEW_SERVER: http://codereview.chromium.org/
2 | VIEW_VC: https://github.com/dart-lang/barback/commit/
3 | CC_LIST: reviews@dartlang.org
--------------------------------------------------------------------------------
/example/markdown_converter/README.md:
--------------------------------------------------------------------------------
1 | This example shows how to write a simple pub transformer.
2 |
3 | For more information, see Writing a Pub Transformer:
4 | https://www.dartlang.org/tools/pub/transformers/
5 |
--------------------------------------------------------------------------------
/example/simple_transformer/README.md:
--------------------------------------------------------------------------------
1 | This example shows how to write a simple pub transformer.
2 |
3 | For more information, see Writing a Pub Transformer:
4 | https://www.dartlang.org/tools/pub/transformers/
5 |
--------------------------------------------------------------------------------
/example/aggregate_transformer/README.md:
--------------------------------------------------------------------------------
1 | This example shows how to write an aggregate transformer.
2 |
3 | For more information, see Writing an Aggregate Transformer at:
4 | https://www.dartlang.org/tools/pub/transformers/aggregate.html
5 |
--------------------------------------------------------------------------------
/example/markdown_converter/lib/index.markdown:
--------------------------------------------------------------------------------
1 |
2 | # First level header
3 |
4 | * one
5 | * two
6 | * three
7 |
8 | ## Second level header
9 |
10 | 1. apple
11 | 2. banana
12 | 3. pear
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Don’t commit the following directories created by pub.
2 | .buildlog
3 | .pub/
4 | build/
5 | packages
6 | .packages
7 |
8 | # Or the files created by dart2js.
9 | *.dart.js
10 | *.js_
11 | *.js.deps
12 | *.js.map
13 |
14 | # Include when developing application packages.
15 | pubspec.lock
--------------------------------------------------------------------------------
/example/lazy_transformer/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: lazy_transformer
2 | description: This example implements a very simple lazy transformer
3 | which implements a ROT13 converter. ROT13 (ROTate by 13 spaces) is
4 | a classic substitution cipher where each letter is replaced by
5 | the letter 13 places later in the alphabet.
6 |
7 | dependencies:
8 | barback: ">=0.14.1 <0.16.0"
9 |
10 | transformers:
11 | - lazy_transformer
12 |
--------------------------------------------------------------------------------
/example/aggregate_transformer/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: aggregate_transformer
2 | description: This example implements an aggregate transformer.
3 | It collects recipes, stored as incomplete HTML files, into
4 | a single, complete HTML file.
5 |
6 | dependencies:
7 | barback: ">=0.14.1 <0.16.0"
8 |
9 | # Override the barback dependency so this example always uses the version
10 | # of barback it's bundled with.
11 | dependency_overrides:
12 | barback: {path: ../..}
13 |
14 | transformers:
15 | - aggregate_transformer
16 |
--------------------------------------------------------------------------------
/example/simple_transformer/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: simple_transformer
2 | description: This hello world example implements a simple
3 | transformer that inserts a copyright string into
4 | an input asset - a file that ends with ".txt".
5 |
6 | dependencies:
7 | barback: ">=0.14.1 <0.16.0"
8 |
9 | # Override the barback dependency so this example always uses the version
10 | # of barback it's bundled with.
11 | dependency_overrides:
12 | barback: {path: ../..}
13 |
14 | transformers:
15 | - simple_transformer
16 |
--------------------------------------------------------------------------------
/example/markdown_converter/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: markdown_converter
2 | description: This hello world example implements a simple
3 | transformer that converts a markdown file (with
4 | a ".mdown", ".md", or a ".markdown" extension) to HTML.
5 |
6 | dependencies:
7 | barback: ">=0.14.1 <0.16.0"
8 | markdown: any
9 |
10 | # Override the barback dependency so this example always uses the version
11 | # of barback it's bundled with.
12 | dependency_overrides:
13 | barback: {path: ../..}
14 |
15 | transformers:
16 | - markdown_converter
17 |
--------------------------------------------------------------------------------
/example/lazy_transformer/lib/message.txt:
--------------------------------------------------------------------------------
1 | Dear Mom,
2 |
3 | Hi! How are you? Sorry that I haven't written recently,
4 | but I hope all is well!
5 |
6 | Life is great at University. I'm sure that I can bring
7 | up my D- in French.
8 |
9 | Also, the Dean's office says they won't press charges.
10 | It was a big todo about nothing - just your typical college
11 | prank and the badger (UFE's mascot) was eating pretty
12 | well on my lunch card. And some spackle will fix the
13 | damage to my dorm room walls right up...
14 |
15 | By the way, can I have a loan? $300 would be great.
16 |
17 | Thanks so much and I love you!
18 |
19 | Your loving son,
20 |
21 | Rodger
22 |
--------------------------------------------------------------------------------
/test/transformer/lazy_rewrite.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.lazy_rewrite;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'declaring_rewrite.dart';
10 |
11 | /// Like [RewriteTransformer], but returns a lazy asset that doesn't perform the
12 | /// rewrite until it's materialized.
13 | class LazyRewriteTransformer extends DeclaringRewriteTransformer
14 | implements LazyTransformer {
15 | LazyRewriteTransformer(String from, String to) : super(from, to);
16 | }
17 |
--------------------------------------------------------------------------------
/test/transformer/lazy_assets.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.lazy_assets;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'declare_assets.dart';
10 |
11 | /// Like [DeclareAssetsTransformer], but lazy.
12 | class LazyAssetsTransformer extends DeclareAssetsTransformer
13 | implements LazyTransformer {
14 | LazyAssetsTransformer(Iterable declared,
15 | {Iterable emitted, String input})
16 | : super(declared, emitted: emitted, input: input);
17 | }
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **DEPRECATED**
2 |
3 | The [pub][] transformer system will be removed in Dart 2.
4 | See the [Dart 2 Migration Guide](https://webdev.dartlang.org/dart-2) for
5 | guidance.
6 |
7 | ---
8 |
9 | Barback is an asset build system. It is the library underlying
10 | [pub][]'s asset transformers in
11 | `pub build` and `pub serve`.
12 |
13 | Given a set of input files and a set of transformations (think compilers,
14 | preprocessors and the like), it will automatically apply the appropriate
15 | transforms and generate output files. When inputs are modified, it automatically
16 | runs the transforms that are affected.
17 |
18 | To learn more, see [here][].
19 |
20 | [pub]: https://www.dartlang.org/tools/pub/get-started
21 | [here]: https://www.dartlang.org/tools/pub/assets-and-transformers
22 |
--------------------------------------------------------------------------------
/test/transformer/emit_nothing.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.emit_nothing;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'mock.dart';
10 |
11 | /// A transformer that emits no assets.
12 | class EmitNothingTransformer extends MockTransformer {
13 | final String extension;
14 |
15 | EmitNothingTransformer(this.extension);
16 |
17 | bool doIsPrimary(AssetId id) => id.extension == ".$extension";
18 |
19 | void doApply(Transform transform) {
20 | // Emit nothing.
21 | }
22 |
23 | String toString() => "$extension->nothing";
24 | }
25 |
--------------------------------------------------------------------------------
/test/transformer/create_asset.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.create_asset;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'mock.dart';
10 |
11 | /// A transformer that outputs an asset with the given id.
12 | class CreateAssetTransformer extends MockTransformer {
13 | final String output;
14 |
15 | CreateAssetTransformer(this.output);
16 |
17 | bool doIsPrimary(AssetId id) => true;
18 |
19 | void doApply(Transform transform) {
20 | transform
21 | .addOutput(new Asset.fromString(new AssetId.parse(output), output));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/lazy_transformer/README.md:
--------------------------------------------------------------------------------
1 | This example shows how to write a lazy transformer.
2 |
3 | This lazy tranformer implements a ROT13 converter. ROT13 (ROTate 13)
4 | is a classic substitution cipher which replaces each letter in the source
5 | file with the corresponding letter 13 places later in the alphabet.
6 | The source file should have a ".txt" extension and the converted file
7 | is created with a ".shhhh" extension.
8 |
9 | Generally, only transformers that take a long time to run should be made lazy.
10 | This transformer is not particularly slow, but imagine that it might be used
11 | to convert the entire library of congress–laziness would then be a virtue.
12 |
13 | For more information, see Writing a Lazy Transformer at:
14 | https://www.dartlang.org/tools/pub/transformers/lazy-transformer.html
15 |
--------------------------------------------------------------------------------
/test/transformer/lazy_aggregate_many_to_many.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.lazy_aggregate_many_to_many;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'declaring_aggregate_many_to_many.dart';
10 |
11 | /// An [AggregateTransformer] that takes all assets in each directory with a
12 | /// given extension and adds to their contents.
13 | class LazyAggregateManyToManyTransformer
14 | extends DeclaringAggregateManyToManyTransformer
15 | implements LazyAggregateTransformer {
16 | LazyAggregateManyToManyTransformer(String extension) : super(extension);
17 | }
18 |
--------------------------------------------------------------------------------
/test/barback_mode_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.barback_mode_test;
6 |
7 | import 'package:barback/barback.dart';
8 | import 'package:unittest/unittest.dart';
9 |
10 | import 'utils.dart';
11 |
12 | main() {
13 | initConfig();
14 | test("constructor uses canonical instances for DEBUG and RELEASE", () {
15 | expect(
16 | identical(BarbackMode.DEBUG, new BarbackMode(BarbackMode.DEBUG.name)),
17 | isTrue);
18 | expect(
19 | identical(
20 | BarbackMode.RELEASE, new BarbackMode(BarbackMode.RELEASE.name)),
21 | isTrue);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/lib/src/transformer/lazy_transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.lazy_transformer;
6 |
7 | import 'declaring_transformer.dart';
8 |
9 | /// An interface for [Transformer]s that indicates that the transformer's
10 | /// outputs shouldn't be generated until requested.
11 | ///
12 | /// The [declareOutputs] method is used to figure out which assets should be
13 | /// treated as "lazy." Lazy assets will only be forced to be generated if
14 | /// they're requested by the user or if they're used by a non-declaring
15 | /// transformer.
16 | abstract class LazyTransformer extends DeclaringTransformer {}
17 |
--------------------------------------------------------------------------------
/lib/src/internal_asset.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | /// This library exists so that pub can retain backwards-compatibility with
6 | /// versions of barback prior to 0.13.1.
7 | ///
8 | /// In 0.13.1, `lib/src/internal_asset.dart` was moved to
9 | /// `lib/src/asset/internal_asset.dart. Pub needs to support all versions of
10 | /// barback back through 0.13.0, though. In order for this to work, it needs to
11 | /// be able to import "package:barback/src/internal_asset.dart" on all available
12 | /// barback versions, hence the existence of this library.
13 | library barback.internal_asset;
14 |
15 | export 'asset/internal_asset.dart';
16 |
--------------------------------------------------------------------------------
/test/transformer/lazy_aggregate_many_to_one.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.lazy_aggregate_many_to_one;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'declaring_aggregate_many_to_one.dart';
10 |
11 | /// Like [AggregateManyToOneTransformer], but returns a lazy asset that doesn't
12 | /// perform the rewrite until it's materialized.
13 | class LazyAggregateManyToOneTransformer
14 | extends DeclaringAggregateManyToOneTransformer
15 | implements LazyAggregateTransformer {
16 | LazyAggregateManyToOneTransformer(String extension, String output)
17 | : super(extension, output);
18 | }
19 |
--------------------------------------------------------------------------------
/.status:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | # for details. All rights reserved. Use of this source code is governed by a
3 | # BSD-style license that can be found in the LICENSE file.
4 |
5 | # Skip non-test files ending with "_test".
6 | packages/*: Skip
7 | */packages/*: Skip
8 | */*/packages/*: Skip
9 | */*/*/packages/*: Skip
10 | */*/*/*packages/*: Skip
11 | */*/*/*/*packages/*: Skip
12 |
13 | # Only run tests from the build directory, since we don't care about the
14 | # difference between transformed an untransformed code.
15 | test/*: Skip
16 |
17 | [ $runtime == vm && $mode == debug]
18 | build/test/package_graph/repetition_test: Skip # Times out
19 |
20 | [ $runtime == vm && $arch == simarm ]
21 | build/test/too_many_open_files_test: Skip # 14220
22 |
23 | [ $browser ]
24 | *: Fail, OK # Uses dart:io.
25 |
--------------------------------------------------------------------------------
/test/transformer/lazy_many_to_one.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.lazy_many_to_one;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'many_to_one.dart';
10 |
11 | /// Like [ManyToOneTransformer], but returns a lazy asset that doesn't perform
12 | /// the conglomeration until it's materialized.
13 | class LazyManyToOneTransformer extends ManyToOneTransformer
14 | implements LazyTransformer {
15 | LazyManyToOneTransformer(String extension) : super(extension);
16 |
17 | void declareOutputs(DeclaringTransform transform) {
18 | transform.declareOutput(transform.primaryId.changeExtension(".out"));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/src/transformer/lazy_aggregate_transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.lazy_aggregate_transformer;
6 |
7 | import 'declaring_aggregate_transformer.dart';
8 |
9 | /// An interface for [AggregateTransformer]s that indicates that the
10 | /// transformer's outputs shouldn't be generated until requested.
11 | ///
12 | /// The [declareOutputs] method is used to figure out which assets should be
13 | /// treated as "lazy." Lazy assets will only be forced to be generated if
14 | /// they're requested by the user or if they're used by a non-declaring
15 | /// transformer.
16 | abstract class LazyAggregateTransformer extends DeclaringAggregateTransformer {}
17 |
--------------------------------------------------------------------------------
/example/aggregate_transformer/lib/recipes-grammas/winter-squash-pie-recipe.html:
--------------------------------------------------------------------------------
1 |
2 |
1-1/2 cups winter squash (such as pumpkin), pureed
12 |
1 9" (single) pie crust
13 |
14 |
15 | Mix eggs and sugar together. Add pumpkin and mix. Add salt and spices.
16 | Slowly stir in evaporated milk, and then the cream.
17 |
18 | Pour into un nbaked pie crust. Bake in a preheated 425°F oven
19 | for 15 minutes. Turn heat down to 325°F and bake another 45 minutes
20 | until center is firm.
21 |
22 | Remove from oven and cool on a rack before serving.
23 |
24 | Serve with whipped cream.
25 |
--------------------------------------------------------------------------------
/test/transformer/declaring_aggregate_many_to_many.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.declaring_aggregate_many_to_many;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'aggregate_many_to_many.dart';
12 |
13 | /// Like [AggregateManyToManyTransformer], but declares its assets ahead of
14 | /// time.
15 | class DeclaringAggregateManyToManyTransformer
16 | extends AggregateManyToManyTransformer
17 | implements DeclaringAggregateTransformer {
18 | DeclaringAggregateManyToManyTransformer(String extension) : super(extension);
19 |
20 | Future declareOutputs(DeclaringAggregateTransform transform) =>
21 | transform.primaryIds.asyncMap(transform.declareOutput).toList();
22 | }
23 |
--------------------------------------------------------------------------------
/test/transformer/lazy_check_content_and_rename.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.lazy_check_content_and_rename;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'declaring_check_content_and_rename.dart';
10 |
11 | class LazyCheckContentAndRenameTransformer
12 | extends DeclaringCheckContentAndRenameTransformer
13 | implements LazyTransformer {
14 | LazyCheckContentAndRenameTransformer(
15 | {String oldExtension,
16 | String oldContent,
17 | String newExtension,
18 | String newContent})
19 | : super(
20 | oldExtension: oldExtension,
21 | oldContent: oldContent,
22 | newExtension: newExtension,
23 | newContent: newContent);
24 | }
25 |
--------------------------------------------------------------------------------
/test/transformer/declaring_rewrite.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.declaring_rewrite;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'rewrite.dart';
10 |
11 | /// Like [RewriteTransformer], but declares its assets ahead of time.
12 | class DeclaringRewriteTransformer extends RewriteTransformer
13 | implements DeclaringTransformer {
14 | DeclaringRewriteTransformer(String from, String to) : super(from, to);
15 |
16 | void declareOutputs(DeclaringTransform transform) {
17 | if (consumePrimary) transform.consumePrimary();
18 | for (var extension in to.split(" ")) {
19 | var id = transform.primaryId.changeExtension(".$extension");
20 | transform.declareOutput(id);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test/transformer/declaring_aggregate_many_to_one.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.declaring_aggregate_many_to_one;
6 |
7 | import 'package:barback/barback.dart';
8 | import 'package:path/path.dart' as path;
9 |
10 | import 'aggregate_many_to_one.dart';
11 |
12 | /// Like [AggregateManyToOneTransformer], but declares its assets ahead of time.
13 | class DeclaringAggregateManyToOneTransformer
14 | extends AggregateManyToOneTransformer
15 | implements DeclaringAggregateTransformer {
16 | DeclaringAggregateManyToOneTransformer(String extension, String output)
17 | : super(extension, output);
18 |
19 | void declareOutputs(DeclaringAggregateTransform transform) {
20 | transform.declareOutput(
21 | new AssetId(transform.package, path.url.join(transform.key, output)));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test/transformer/bad_log.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.bad_log;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'mock.dart';
10 |
11 | /// A transformer that logs an error when run, Before generating the given
12 | /// outputs.
13 | class BadLogTransformer extends MockTransformer {
14 | /// The list of asset names that it should output.
15 | final List outputs;
16 |
17 | BadLogTransformer(this.outputs);
18 |
19 | bool doIsPrimary(AssetId id) => true;
20 |
21 | void doApply(Transform transform) {
22 | transform.logger.error("first error");
23 | transform.logger.error("second error");
24 |
25 | for (var output in outputs) {
26 | var id = new AssetId.parse(output);
27 | transform.addOutput(new Asset.fromString(id, output));
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test/transformer/check_content.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.check_content;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'mock.dart';
12 |
13 | /// A transformer that modifies assets that contains the given content.
14 | class CheckContentTransformer extends MockTransformer {
15 | final Pattern content;
16 | final String addition;
17 |
18 | CheckContentTransformer(this.content, this.addition);
19 |
20 | bool doIsPrimary(AssetId id) => true;
21 |
22 | Future doApply(Transform transform) {
23 | return getPrimary(transform).then((primary) {
24 | return primary.readAsString().then((value) {
25 | if (!value.contains(content)) return;
26 |
27 | transform
28 | .addOutput(new Asset.fromString(primary.id, "$value$addition"));
29 | });
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example/simple_transformer/lib/transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | import 'package:barback/barback.dart';
6 |
7 | import 'dart:async';
8 |
9 | class InsertCopyright extends Transformer {
10 | String copyright = "Copyright (c) 2014, the Example project authors.\n";
11 |
12 | // A constructor named "asPlugin" is required. It can be empty, but
13 | // it must be present. It is how pub determines that you want this
14 | // class to be publicly available as a loadable transformer plugin.
15 | InsertCopyright.asPlugin();
16 |
17 | Future isPrimary(AssetId id) async => id.extension == '.txt';
18 |
19 | Future apply(Transform transform) async {
20 | var content = await transform.primaryInput.readAsString();
21 | var id = transform.primaryInput.id;
22 | var newContent = copyright + content;
23 | transform.addOutput(new Asset.fromString(id, newContent));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/transformer/conditionally_consume_primary.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.conditionally_consume_primary;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'rewrite.dart';
12 |
13 | /// A transformer that consumes its primary input only if its contents match a
14 | /// given pattern.
15 | class ConditionallyConsumePrimaryTransformer extends RewriteTransformer {
16 | final Pattern content;
17 |
18 | ConditionallyConsumePrimaryTransformer(String from, String to, this.content)
19 | : super(from, to);
20 |
21 | Future doApply(Transform transform) {
22 | return getPrimary(transform).then((primary) {
23 | return primary.readAsString().then((value) {
24 | if (value.contains(content)) transform.consumePrimary();
25 | return super.doApply(transform);
26 | });
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/transformer/bad.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.bad;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'mock.dart';
10 |
11 | /// A transformer that throws an exception when run, after generating the
12 | /// given outputs.
13 | class BadTransformer extends MockTransformer {
14 | /// The error it throws.
15 | static const ERROR = "I am a bad transformer!";
16 |
17 | /// The list of asset names that it should output.
18 | final List outputs;
19 |
20 | BadTransformer(this.outputs);
21 |
22 | bool doIsPrimary(AssetId id) => true;
23 |
24 | void doApply(Transform transform) {
25 | // Create the outputs first.
26 | for (var output in outputs) {
27 | var id = new AssetId.parse(output);
28 | transform.addOutput(new Asset.fromString(id, output));
29 | }
30 |
31 | // Then fail.
32 | throw ERROR;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/transformer/declaring_check_content_and_rename.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.declaring_check_content_and_rename;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'check_content_and_rename.dart';
10 |
11 | class DeclaringCheckContentAndRenameTransformer
12 | extends CheckContentAndRenameTransformer implements DeclaringTransformer {
13 | DeclaringCheckContentAndRenameTransformer(
14 | {String oldExtension,
15 | String oldContent,
16 | String newExtension,
17 | String newContent})
18 | : super(
19 | oldExtension: oldExtension,
20 | oldContent: oldContent,
21 | newExtension: newExtension,
22 | newContent: newContent);
23 |
24 | void declareOutputs(DeclaringTransform transform) {
25 | transform
26 | .declareOutput(transform.primaryId.changeExtension('.$newExtension'));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/example/aggregate_transformer/lib/recipes-grammas/banana-pudding-recipe.html:
--------------------------------------------------------------------------------
1 |
2 |
15 | Add water to saucepan. Whisk in flour until smooth. Add sugar
16 | and mix well. Add eggs, one at a time, mixing well after each one.
17 | Slowly stir in milk, mixing well.
18 |
19 | Place pan on low heat. Cook, stirring constantly, until mixture
20 | is thickened to the consistency of a thick gravy, approximately
21 | 20 minutes. The mixture will start to steam and produce bubbles,
22 | but you don't want a full boil.
23 |
24 | Remove from heat and stir in vanilla. Cool thoroughly. Fold in
25 | sour cream and mix well.
26 |
27 | Layer wafers, bananas, and pudding in a glass serving bowl, such
28 | as a trifle bowl. Thoroughly chill. Serve with whipped cream.
29 |
--------------------------------------------------------------------------------
/test/transformer/lazy_bad.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.lazy_bad;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'mock.dart';
10 |
11 | /// A lazy transformer that throws an exception after [declareOutputs].
12 | class LazyBadTransformer extends MockTransformer implements LazyTransformer {
13 | /// The error [this] throws.
14 | static const ERROR = "I am a bad transformer!";
15 |
16 | /// The asset name that [this] should output.
17 | final String output;
18 |
19 | LazyBadTransformer(this.output);
20 |
21 | bool doIsPrimary(AssetId id) => true;
22 |
23 | void doApply(Transform transform) {
24 | var id = new AssetId.parse(output);
25 | transform.addOutput(new Asset.fromString(id, output));
26 | }
27 |
28 | void declareOutputs(DeclaringTransform transform) {
29 | var id = new AssetId.parse(output);
30 | transform.declareOutput(id);
31 | throw ERROR;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/test/package_graph/many_parallel_transformers_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.package_graph.transform_test;
6 |
7 | import 'package:barback/src/utils.dart';
8 | import 'package:scheduled_test/scheduled_test.dart';
9 |
10 | import '../utils.dart';
11 |
12 | main() {
13 | initConfig();
14 |
15 | test("handles many parallel transformers", () {
16 | currentSchedule.timeout *= 3;
17 | var files = new List.generate(100, (i) => "app|$i.txt");
18 | var rewrite = new RewriteTransformer("txt", "out");
19 | initGraph(files, {
20 | "app": [
21 | [rewrite]
22 | ]
23 | });
24 |
25 | // Pause and resume apply to simulate parallel long-running transformers.
26 | rewrite.pauseApply();
27 | updateSources(files);
28 | schedule(pumpEventQueue);
29 | rewrite.resumeApply();
30 |
31 | for (var i = 0; i < 100; i++) {
32 | expectAsset("app|$i.out", "$i.out");
33 | }
34 | buildShouldSucceed();
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/test/transformer/has_input.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.has_input;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'mock.dart';
12 |
13 | /// Overwrites its primary inputs with descriptions of whether various secondary
14 | /// inputs exist.
15 | class HasInputTransformer extends MockTransformer {
16 | /// The inputs whose existence will be checked.
17 | final List inputs;
18 |
19 | HasInputTransformer(Iterable inputs)
20 | : inputs = inputs.map((input) => new AssetId.parse(input)).toList();
21 |
22 | bool doIsPrimary(AssetId id) => true;
23 |
24 | Future doApply(Transform transform) {
25 | return Future.wait(inputs.map((input) {
26 | return transform.hasInput(input).then((hasInput) => "$input: $hasInput");
27 | })).then((results) {
28 | transform.addOutput(
29 | new Asset.fromString(transform.primaryInput.id, results.join(', ')));
30 | });
31 | }
32 |
33 | String toString() => "has inputs $inputs";
34 | }
35 |
--------------------------------------------------------------------------------
/test/transformer/sync_rewrite.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.sync_rewrite;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | /// Like [DeclaringRewriteTransformer], but with no methods returning Futures.
10 | class SyncRewriteTransformer extends Transformer
11 | implements DeclaringTransformer {
12 | final String from;
13 | final String to;
14 |
15 | SyncRewriteTransformer(this.from, this.to);
16 |
17 | bool isPrimary(AssetId id) => id.extension == ".$from";
18 |
19 | void apply(Transform transform) {
20 | for (var extension in to.split(" ")) {
21 | var id = transform.primaryInput.id.changeExtension(".$extension");
22 | transform.addOutput(new Asset.fromString(id, "new.$extension"));
23 | }
24 | }
25 |
26 | void declareOutputs(DeclaringTransform transform) {
27 | for (var extension in to.split(" ")) {
28 | var id = transform.primaryId.changeExtension(".$extension");
29 | transform.declareOutput(id);
30 | }
31 | }
32 |
33 | String toString() => "$from->$to";
34 | }
35 |
--------------------------------------------------------------------------------
/example/markdown_converter/lib/transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | import 'package:barback/barback.dart';
6 | import 'package:markdown/markdown.dart';
7 |
8 | import 'dart:async';
9 |
10 | class ConvertMarkdown extends Transformer {
11 | // A constructor named "asPlugin" is required. It can be empty, but
12 | // it must be present. It is how pub determines that you want this
13 | // class to be publicly available as a loadable transformer plugin.
14 | ConvertMarkdown.asPlugin();
15 |
16 | // Any markdown file with one of the following extensions is
17 | // converted to HTML.
18 | String get allowedExtensions => ".md .markdown .mdown";
19 |
20 | Future apply(Transform transform) async {
21 | var content = await transform.primaryInput.readAsString();
22 |
23 | // The extension of the output is changed to ".html".
24 | var id = transform.primaryInput.id.changeExtension(".html");
25 |
26 | var newContent =
27 | "" + markdownToHtml(content) + "";
28 | transform.addOutput(new Asset.fromString(id, newContent));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/src/log.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.log;
6 |
7 | import 'package:source_span/source_span.dart';
8 |
9 | import 'asset/asset_id.dart';
10 | import 'errors.dart';
11 |
12 | /// The severity of a logged message.
13 | class LogLevel {
14 | static const INFO = const LogLevel("Info");
15 | static const FINE = const LogLevel("Fine");
16 | static const WARNING = const LogLevel("Warning");
17 | static const ERROR = const LogLevel("Error");
18 |
19 | final String name;
20 | const LogLevel(this.name);
21 |
22 | String toString() => name;
23 | }
24 |
25 | /// One message logged during a transform.
26 | class LogEntry {
27 | /// The transform that logged the message.
28 | final TransformInfo transform;
29 |
30 | /// The asset that the message is associated with.
31 | final AssetId assetId;
32 |
33 | final LogLevel level;
34 | final String message;
35 |
36 | /// The location that the message pertains to or null if not associated with
37 | /// a [SourceSpan].
38 | final SourceSpan span;
39 |
40 | LogEntry(this.transform, this.assetId, this.level, this.message, this.span);
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/transformer/declaring_transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.declaring_transformer;
6 |
7 | import 'dart:async';
8 |
9 | import 'declaring_transform.dart';
10 |
11 | /// An interface for [Transformer]s that can cheaply figure out which assets
12 | /// they'll emit without doing the work of actually creating those assets.
13 | ///
14 | /// If a transformer implements this interface, that allows barback to perform
15 | /// optimizations to make the asset graph work more smoothly.
16 | abstract class DeclaringTransformer {
17 | /// Declare which assets would be emitted for the primary input id specified
18 | /// by [transform].
19 | ///
20 | /// This works a little like [Transformer.apply], with two main differences.
21 | /// First, instead of having access to the primary input's contents, it only
22 | /// has access to its id. Second, instead of emitting [Asset]s, it just emits
23 | /// [AssetId]s through [transform.addOutputId].
24 | ///
25 | /// If this does asynchronous work, it should return a [Future] that completes
26 | /// once it's finished.
27 | declareOutputs(DeclaringTransform transform);
28 | }
29 |
--------------------------------------------------------------------------------
/test/transformer/one_to_many.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.one_to_many;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'mock.dart';
12 |
13 | /// A [Transformer] that takes an input asset that contains a comma-separated
14 | /// list of paths and outputs a file for each path.
15 | class OneToManyTransformer extends MockTransformer {
16 | final String extension;
17 |
18 | /// Creates a transformer that consumes assets with [extension].
19 | ///
20 | /// That file contains a comma-separated list of paths and it will output
21 | /// files at each of those paths.
22 | OneToManyTransformer(this.extension);
23 |
24 | bool doIsPrimary(AssetId id) => id.extension == ".$extension";
25 |
26 | Future doApply(Transform transform) async {
27 | var lines = await (await getPrimary(transform)).readAsString();
28 | for (var line in lines.split(",")) {
29 | var id = new AssetId(transform.primaryInput.id.package, line);
30 | transform.addOutput(new Asset.fromString(id, "spread $extension"));
31 | }
32 | }
33 |
34 | String toString() => "1->many $extension";
35 | }
36 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: barback
2 |
3 | # Note! This version number is referenced directly in the pub source code in
4 | # lib/src/barback.dart. Pub implicitly places a version constraint on barback to
5 | # ensure users only select a version of barback that works with their current
6 | # version of pub.
7 | #
8 | # When the minor or patch version of this is upgraded, you *must* update that
9 | # version constraint in pub to stay in sync with this.
10 | version: 0.15.2+16
11 |
12 | author: "Dart Team "
13 | homepage: http://github.com/dart-lang/barback
14 | description: >
15 | A DEPRECATED asset build system for Dart.
16 |
17 | Given a set of input files and a set of transformations (think compilers,
18 | preprocessors and the like), will automatically apply the appropriate
19 | transforms and generate output files. When inputs are modified, automatically
20 | runs the transforms that are affected.
21 |
22 | Runs transforms asynchronously and in parallel when possible to maximize
23 | responsiveness.
24 | dependencies:
25 | async: ">=1.10.0 <3.0.0"
26 | path: ">=0.9.0 <2.0.0"
27 | pool: ">=1.0.0 <2.0.0"
28 | source_span: ">=1.0.0 <2.0.0"
29 | stack_trace: ">=0.9.1 <2.0.0"
30 | collection: "^1.5.0"
31 | dev_dependencies:
32 | scheduled_test: ">=0.9.0 <0.11.0"
33 | unittest: ">=0.9.0 <0.10.0"
34 | environment:
35 | sdk: ">=2.0.0-dev.17.0 <2.0.0"
36 |
--------------------------------------------------------------------------------
/test/transformer/aggregate_many_to_many.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.aggregate_many_to_many;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 | import 'package:path/path.dart' as path;
11 |
12 | import 'mock_aggregate.dart';
13 |
14 | /// An [AggregateTransformer] that takes all assets with a given extension,
15 | /// grouped by directory, adds to their contents.
16 | class AggregateManyToManyTransformer extends MockAggregateTransformer {
17 | /// The extension of assets to combine.
18 | final String extension;
19 |
20 | AggregateManyToManyTransformer(this.extension);
21 |
22 | String doClassifyPrimary(AssetId id) {
23 | if (id.extension != ".$extension") return null;
24 | return path.url.dirname(id.path);
25 | }
26 |
27 | Future doApply(AggregateTransform transform) {
28 | return getPrimaryInputs(transform).asyncMap((asset) {
29 | return asset.readAsString().then((contents) {
30 | transform
31 | .addOutput(new Asset.fromString(asset.id, "modified $contents"));
32 | });
33 | }).toList();
34 | }
35 |
36 | String toString() => "aggregate $extension->many";
37 | }
38 |
--------------------------------------------------------------------------------
/test/transformer/catch_asset_not_found.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.catch_asset_not_found;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'mock.dart';
12 |
13 | /// A transformer that tries to load a secondary input and catches an
14 | /// [AssetNotFoundException] if the input doesn't exist.
15 | class CatchAssetNotFoundTransformer extends MockTransformer {
16 | /// The extension of assets this applies to.
17 | final String extension;
18 |
19 | /// The id of the secondary input to load.
20 | final AssetId input;
21 |
22 | CatchAssetNotFoundTransformer(this.extension, String input)
23 | : input = new AssetId.parse(input);
24 |
25 | bool doIsPrimary(AssetId id) => id.extension == extension;
26 |
27 | Future doApply(Transform transform) {
28 | return transform.getInput(input).then((_) {
29 | transform.addOutput(
30 | new Asset.fromString(transform.primaryInput.id, "success"));
31 | }).catchError((e) {
32 | if (e is! AssetNotFoundException) throw e;
33 | transform.addOutput(new Asset.fromString(
34 | transform.primaryInput.id, "failed to load $input"));
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/test/transformer/log.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.log;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'mock.dart';
10 |
11 | /// A transformer that logs given entries during its apply.
12 | class LogTransformer extends MockTransformer {
13 | /// The list of entries that it should log.
14 | ///
15 | /// Each entry has the log level followed by the message, like:
16 | ///
17 | /// error: This is the error message.
18 | final List _entries;
19 |
20 | LogTransformer(this._entries);
21 |
22 | bool doIsPrimary(AssetId id) => true;
23 |
24 | void doApply(Transform transform) {
25 | for (var entry in _entries) {
26 | var parts = entry.split(":");
27 | var logFn;
28 | switch (parts[0]) {
29 | case "error":
30 | logFn = transform.logger.error;
31 | break;
32 | case "warning":
33 | logFn = transform.logger.warning;
34 | break;
35 | case "info":
36 | logFn = transform.logger.info;
37 | break;
38 | case "fine":
39 | logFn = transform.logger.fine;
40 | break;
41 | }
42 |
43 | logFn(parts[1].trim());
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/static_provider_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.static_provider_test;
6 |
7 | import 'package:scheduled_test/scheduled_test.dart';
8 |
9 | import 'utils.dart';
10 |
11 | main() {
12 | initConfig();
13 | test("gets a static source asset", () {
14 | initStaticGraph(["app|foo.txt"], staticPackages: ["app"]);
15 | expectAsset("app|foo.txt");
16 | buildShouldSucceed();
17 | });
18 |
19 | test("doesn't get a nonexistent static source asset", () {
20 | initStaticGraph(["app|foo.txt"], staticPackages: ["app"]);
21 | expectNoAsset("app|bar.txt");
22 | });
23 |
24 | test("a transformer can see a static asset", () {
25 | initStaticGraph({
26 | "static|b.inc": "b",
27 | "app|a.txt": "static|b.inc"
28 | }, staticPackages: [
29 | "static"
30 | ], transformers: {
31 | "app": [
32 | [new ManyToOneTransformer("txt")]
33 | ]
34 | });
35 | updateSources(["app|a.txt"]);
36 | expectAsset("app|a.out", "b");
37 | });
38 |
39 | test("can list all static assets", () {
40 | initStaticGraph(["app|foo.txt", "app|bar.txt", "app|baz.txt"],
41 | staticPackages: ["app"]);
42 | expectAllAssets(["app|foo.txt", "app|bar.txt", "app|baz.txt"]);
43 | });
44 | }
45 |
--------------------------------------------------------------------------------
/lib/barback.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | @Deprecated(
6 | 'See https://webdev.dartlang.org/dart-2 for details on how to migrate.')
7 | library barback;
8 |
9 | export 'src/asset/asset.dart';
10 | export 'src/asset/asset_id.dart';
11 | export 'src/asset/asset_set.dart';
12 | export 'src/barback.dart';
13 | export 'src/build_result.dart';
14 | export 'src/errors.dart' hide flattenAggregateExceptions;
15 | export 'src/log.dart';
16 | export 'src/package_provider.dart';
17 | export 'src/transformer/aggregate_transform.dart';
18 | export 'src/transformer/aggregate_transformer.dart';
19 | export 'src/transformer/barback_settings.dart';
20 | export 'src/transformer/base_transform.dart';
21 | export 'src/transformer/declaring_aggregate_transform.dart';
22 | export 'src/transformer/declaring_aggregate_transformer.dart';
23 | export 'src/transformer/declaring_transform.dart' hide newDeclaringTransform;
24 | export 'src/transformer/declaring_transformer.dart';
25 | export 'src/transformer/lazy_aggregate_transformer.dart';
26 | export 'src/transformer/lazy_transformer.dart';
27 | export 'src/transformer/transform.dart' hide newTransform;
28 | export 'src/transformer/transform_logger.dart';
29 | export 'src/transformer/transformer.dart';
30 | export 'src/transformer/transformer_group.dart';
31 |
--------------------------------------------------------------------------------
/test/transformer/rewrite.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.rewrite;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'mock.dart';
12 |
13 | /// A [Transformer] that takes assets ending with one extension and generates
14 | /// assets with a given extension.
15 | ///
16 | /// Appends the output extension to the contents of the input file.
17 | class RewriteTransformer extends MockTransformer {
18 | final String from;
19 | final String to;
20 |
21 | /// Creates a transformer that rewrites assets whose extension is [from] to
22 | /// one whose extension is [to].
23 | ///
24 | /// [to] may be a space-separated list in which case multiple outputs will be
25 | /// created for each input.
26 | RewriteTransformer(this.from, this.to);
27 |
28 | bool doIsPrimary(AssetId id) => id.extension == ".$from";
29 |
30 | Future doApply(Transform transform) {
31 | return getPrimary(transform).then((input) {
32 | return Future.wait(to.split(" ").map((extension) {
33 | var id = input.id.changeExtension(".$extension");
34 | return input.readAsString().then((content) {
35 | transform.addOutput(new Asset.fromString(id, "$content.$extension"));
36 | });
37 | }));
38 | });
39 | }
40 |
41 | String toString() => "$from->$to";
42 | }
43 |
--------------------------------------------------------------------------------
/lib/src/transformer/transformer_group.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.transformer_group;
6 |
7 | /// A [TransformerGroup] encapsulates a phased collection of transformers.
8 | ///
9 | /// A transformer group is defined like a collection of phases, such as you
10 | /// might pass to [Barback.updateTransformers]. Input assets are transformed by
11 | /// the first phase, whose results are passed to the second phase, and so on.
12 | ///
13 | /// However, from the perspective of someone using a [TransformerGroup], it
14 | /// works just like a [Transformer]. It can be included in phases, and will run
15 | /// in parallel to other transformers or groups in those phases. Other
16 | /// transformers and groups will be unable to see any intermediate assets that
17 | /// are generated by one phase of the group and consumed by another. Phases
18 | /// after the one containing the group will be able to see its outputs, though.
19 | class TransformerGroup {
20 | /// The phases that comprise this group.
21 | ///
22 | /// Each element of the inner iterable must be either a [Transformer] or a
23 | /// [TransformerGroup].
24 | final Iterable phases;
25 |
26 | TransformerGroup(Iterable phases)
27 | : this.phases = phases.map((phase) => phase.toList()).toList();
28 |
29 | String toString() => "group of $phases";
30 | }
31 |
--------------------------------------------------------------------------------
/test/transformer/check_content_and_rename.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.check_content_and_rename;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'mock.dart';
12 |
13 | /// A transformer that checks the extension and content of an asset, then
14 | /// produces a new asset with a new extension and new content.
15 | class CheckContentAndRenameTransformer extends MockTransformer {
16 | final String oldExtension;
17 | final String oldContent;
18 | final String newExtension;
19 | final String newContent;
20 |
21 | CheckContentAndRenameTransformer(
22 | {this.oldExtension,
23 | this.oldContent,
24 | this.newExtension,
25 | this.newContent}) {
26 | assert(oldExtension != null);
27 | assert(oldContent != null);
28 | assert(newExtension != null);
29 | assert(newContent != null);
30 | }
31 |
32 | bool doIsPrimary(AssetId id) => id.extension == '.$oldExtension';
33 |
34 | Future doApply(Transform transform) {
35 | return getPrimary(transform).then((input) {
36 | return input.readAsString().then((contents) {
37 | if (contents != oldContent) return;
38 |
39 | transform.addOutput(new Asset.fromString(
40 | input.id.changeExtension('.$newExtension'), newContent));
41 | });
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/test/transformer/declaring_bad.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.declaring_bad;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'bad.dart';
10 | import 'mock.dart';
11 |
12 | /// A transformer that throws an exception when run, after generating the
13 | /// given outputs.
14 | class DeclaringBadTransformer extends MockTransformer
15 | implements DeclaringTransformer {
16 | /// Whether this should throw an error in [declareOutputs].
17 | final bool declareError;
18 |
19 | /// Whether this should throw an error in [apply].
20 | final bool applyError;
21 |
22 | /// The id of the output asset to emit.
23 | final AssetId output;
24 |
25 | DeclaringBadTransformer(String output,
26 | {bool declareError: true, bool applyError: false})
27 | : this.output = new AssetId.parse(output),
28 | this.declareError = declareError,
29 | this.applyError = applyError;
30 |
31 | bool doIsPrimary(AssetId id) => true;
32 |
33 | void doApply(Transform transform) {
34 | transform.addOutput(new Asset.fromString(output, "bad out"));
35 | if (applyError) throw BadTransformer.ERROR;
36 | }
37 |
38 | void declareOutputs(DeclaringTransform transform) {
39 | if (consumePrimary) transform.consumePrimary();
40 | transform.declareOutput(output);
41 | if (declareError) throw BadTransformer.ERROR;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2013, the Dart project authors. All rights reserved.
2 | Redistribution and use in source and binary forms, with or without
3 | modification, are permitted provided that the following conditions are
4 | met:
5 |
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above
9 | copyright notice, this list of conditions and the following
10 | disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 | * Neither the name of Google Inc. nor the names of its
13 | contributors may be used to endorse or promote products derived
14 | from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/lib/src/asset/asset_node_set.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.asset.asset_node_set;
6 |
7 | import 'package:collection/collection.dart';
8 |
9 | import 'asset_id.dart';
10 | import 'asset_node.dart';
11 |
12 | /// A set of [AssetNode]s that automatically ensures that nodes are removed from
13 | /// the set as soon as they're marked as [AssetState.REMOVED].
14 | ///
15 | /// Asset nodes may be accessed by their ids. This means that only one node with
16 | /// a given id may be stored in the set at a time.
17 | class AssetNodeSet extends DelegatingSet {
18 | // TODO(nweiz): Use DelegatingMapSet when issue 18705 is fixed.
19 | /// A map from asset ids to assets in the set.
20 | final _assetsById = new Map();
21 |
22 | AssetNodeSet() : super(new Set());
23 |
24 | /// Returns the asset node in the set with [id], or `null` if none exists.
25 | AssetNode operator [](AssetId id) => _assetsById[id];
26 |
27 | bool add(AssetNode node) {
28 | if (node.state.isRemoved) return false;
29 | node.whenRemoved(() {
30 | super.remove(node);
31 | _assetsById.remove(node.id);
32 | });
33 | _assetsById[node.id] = node;
34 | return super.add(node);
35 | }
36 |
37 | /// Returns whether an asset node with the given [id] is in the set.
38 | bool containsId(AssetId id) => _assetsById.containsKey(id);
39 |
40 | void addAll(Iterable nodes) => nodes.forEach(add);
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/transformer/declaring_aggregate_transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.declaring_aggregate_transformer;
6 |
7 | import 'declaring_aggregate_transform.dart';
8 |
9 | /// An interface for [Transformer]s that can cheaply figure out which assets
10 | /// they'll emit without doing the work of actually creating those assets.
11 | ///
12 | /// If a transformer implements this interface, that allows barback to perform
13 | /// optimizations to make the asset graph work more smoothly.
14 | abstract class DeclaringAggregateTransformer {
15 | /// Declare which assets would be emitted for the primary input ids specified
16 | /// by [transform].
17 | ///
18 | /// This works a little like [AggregateTransformer.apply], with two main
19 | /// differences. First, instead of having access to the primary inputs'
20 | /// contents, it only has access to their ids. Second, instead of emitting
21 | /// [Asset]s, it just emits [AssetId]s through [transform.addOutputId].
22 | ///
23 | /// If this does asynchronous work, it should return a [Future] that completes
24 | /// once it's finished.
25 | ///
26 | /// This may complete before [DeclaringAggregateTransform.primaryIds] stream
27 | /// is closed. For example, it may know that each key will only have two
28 | /// inputs associated with it, and so use `transform.primaryIds.take(2)` to
29 | /// access only those inputs' ids.
30 | declareOutputs(DeclaringAggregateTransform transform);
31 | }
32 |
--------------------------------------------------------------------------------
/test/transformer/declare_assets.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.declare_asset;
6 |
7 | import 'package:barback/barback.dart';
8 |
9 | import 'mock.dart';
10 |
11 | /// A transformer that declares some outputs and emits others.
12 | class DeclareAssetsTransformer extends MockTransformer
13 | implements DeclaringTransformer {
14 | /// The assets that the transformer declares that it will emit.
15 | final List declared;
16 |
17 | /// The assets that the transformer actually emits.
18 | ///
19 | /// These assets' contents will be identical to their ids.
20 | final List emitted;
21 |
22 | /// If this is non-`null`, assets are only declared for this input.
23 | final AssetId input;
24 |
25 | DeclareAssetsTransformer(Iterable declared,
26 | {Iterable emitted, String input})
27 | : this.declared = declared.map((id) => new AssetId.parse(id)).toList(),
28 | this.emitted = (emitted == null ? declared : emitted)
29 | .map((id) => new AssetId.parse(id))
30 | .toList(),
31 | this.input = input == null ? null : new AssetId.parse(input);
32 |
33 | bool doIsPrimary(AssetId id) => input == null || id == input;
34 |
35 | void doApply(Transform transform) {
36 | for (var id in emitted) {
37 | transform.addOutput(new Asset.fromString(id, id.toString()));
38 | }
39 | }
40 |
41 | void declareOutputs(DeclaringTransform transform) {
42 | declared.forEach(transform.declareOutput);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/src/utils/cancelable_future.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.utils.cancelable_future;
6 |
7 | import 'dart:async';
8 |
9 | /// A wrapper for [Future] that can be cancelled.
10 | ///
11 | /// When this is cancelled, that means it won't complete either successfully or
12 | /// with an error, regardless of whether the wrapped Future completes.
13 | /// Cancelling this won't stop whatever code is feeding the wrapped future from
14 | /// running.
15 | class CancelableFuture implements Future {
16 | bool _canceled = false;
17 | final _completer = new Completer.sync();
18 |
19 | CancelableFuture(Future inner) {
20 | inner.then((result) {
21 | if (_canceled) return;
22 | _completer.complete(result);
23 | }).catchError((error, stackTrace) {
24 | if (_canceled) return;
25 | _completer.completeError(error, stackTrace);
26 | });
27 | }
28 |
29 | Stream asStream() => _completer.future.asStream();
30 | Future catchError(Function onError, {bool test(Object error)}) =>
31 | _completer.future.catchError(onError, test: test);
32 | Future then(FutureOr onValue(T value), {Function onError}) =>
33 | _completer.future.then(onValue, onError: onError);
34 | Future whenComplete(action()) => _completer.future.whenComplete(action);
35 | Future timeout(Duration timeLimit, {void onTimeout()}) =>
36 | _completer.future.timeout(timeLimit, onTimeout: onTimeout);
37 |
38 | /// Cancels this future.
39 | void cancel() {
40 | _canceled = true;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/src/asset/asset_forwarder.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.asset.asset_forwarder;
6 |
7 | import 'dart:async';
8 |
9 | import 'asset_node.dart';
10 |
11 | /// A wrapper for an [AssetNode] that forwards events to a new node.
12 | ///
13 | /// A forwarder is used when a class wants to forward an [AssetNode] that it
14 | /// gets as an input, but also wants to have control over when that node is
15 | /// marked as removed. The forwarder can be closed, thus removing its output
16 | /// node, without the original node having been removed.
17 | class AssetForwarder {
18 | /// The subscription on the input node.
19 | StreamSubscription _subscription;
20 |
21 | /// The controller for the output node.
22 | final AssetNodeController _controller;
23 |
24 | /// The node to which events are forwarded.
25 | AssetNode get node => _controller.node;
26 |
27 | AssetForwarder(AssetNode node)
28 | : _controller = new AssetNodeController.from(node) {
29 | if (node.state.isRemoved) return;
30 |
31 | _subscription = node.onStateChange.listen((state) {
32 | if (state.isAvailable) {
33 | _controller.setAvailable(node.asset);
34 | } else if (state.isDirty) {
35 | _controller.setDirty();
36 | } else {
37 | assert(state.isRemoved);
38 | close();
39 | }
40 | });
41 | }
42 |
43 | /// Closes the forwarder and marks [node] as removed.
44 | void close() {
45 | if (_controller.node.state.isRemoved) return;
46 | _subscription.cancel();
47 | _controller.setRemoved();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/example/lazy_transformer/lib/transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | import 'package:barback/barback.dart';
6 |
7 | import 'dart:async';
8 |
9 | class CodedMessageConverter extends Transformer implements LazyTransformer {
10 | // A constructor named "asPlugin" is required. It can be empty, but
11 | // it must be present.
12 | CodedMessageConverter.asPlugin();
13 |
14 | Future isPrimary(AssetId id) async => id.extension == '.txt';
15 |
16 | void declareOutputs(DeclaringTransform transform) {
17 | transform.declareOutput(transform.primaryId.changeExtension('.shhhhh'));
18 | }
19 |
20 | Future apply(Transform transform) async {
21 | var content = await transform.primaryInput.readAsString();
22 |
23 | // The output file is created with the '.shhhhh' extension.
24 | var id = transform.primaryInput.id.changeExtension('.shhhhh');
25 |
26 | var newContent = new StringBuffer();
27 | for (var i = 0; i < content.length; i++) {
28 | newContent.write(rot13(content[i]));
29 | }
30 | transform.addOutput(new Asset.fromString(id, newContent.toString()));
31 | }
32 |
33 | rot13(var ch) {
34 | var c = ch.codeUnitAt(0);
35 | if (c >= 'a'.codeUnitAt(0) && c <= 'm'.codeUnitAt(0)) {
36 | c += 13;
37 | } else if (c >= 'A'.codeUnitAt(0) && c <= 'M'.codeUnitAt(0)) {
38 | c += 13;
39 | } else if (c >= 'n'.codeUnitAt(0) && c <= 'z'.codeUnitAt(0)) {
40 | c -= 13;
41 | } else if (c >= 'N'.codeUnitAt(0) && c <= 'Z'.codeUnitAt(0)) {
42 | c -= 13;
43 | }
44 | return new String.fromCharCode(c);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/src/asset/asset.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.asset.asset;
6 |
7 | import 'dart:async';
8 | import 'dart:io';
9 | import 'dart:convert';
10 |
11 | import 'asset_id.dart';
12 | import 'internal_asset.dart';
13 |
14 | /// A blob of content.
15 | ///
16 | /// Assets may come from the file system, or as the output of a [Transformer].
17 | /// They are identified by [AssetId].
18 | ///
19 | /// Custom implementations of [Asset] are not currently supported.
20 | abstract class Asset {
21 | /// The ID for this asset.
22 | final AssetId id;
23 |
24 | factory Asset.fromBytes(AssetId id, List bytes) =>
25 | new BinaryAsset(id, bytes);
26 |
27 | factory Asset.fromFile(AssetId id, File file) => new FileAsset(id, file.path);
28 |
29 | factory Asset.fromString(AssetId id, String content) =>
30 | new StringAsset(id, content);
31 |
32 | factory Asset.fromPath(AssetId id, String path) => new FileAsset(id, path);
33 |
34 | factory Asset.fromStream(AssetId id, Stream> stream) =>
35 | new StreamAsset(id, stream);
36 |
37 | /// Returns the contents of the asset as a string.
38 | ///
39 | /// If the asset was created from a [String] the original string is always
40 | /// returned and [encoding] is ignored. Otherwise, the binary data of the
41 | /// asset is decoded using [encoding], which defaults to [utf8].
42 | Future readAsString({Encoding encoding});
43 |
44 | /// Streams the binary contents of the asset.
45 | ///
46 | /// If the asset was created from a [String], this returns its UTF-8 encoding.
47 | Stream> read();
48 | }
49 |
--------------------------------------------------------------------------------
/test/transformer/many_to_one.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.many_to_one;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 |
11 | import 'mock.dart';
12 |
13 | /// A transformer that uses the contents of a file to define the other inputs.
14 | ///
15 | /// Outputs a file with the same name as the primary but with an "out"
16 | /// extension containing the concatenated contents of all non-primary inputs.
17 | class ManyToOneTransformer extends MockTransformer {
18 | final String extension;
19 |
20 | /// Creates a transformer that consumes assets with [extension].
21 | ///
22 | /// That file contains a comma-separated list of paths and it will input
23 | /// files at each of those paths.
24 | ManyToOneTransformer(this.extension);
25 |
26 | bool doIsPrimary(AssetId id) => id.extension == ".$extension";
27 |
28 | Future doApply(Transform transform) async {
29 | var primary = await getPrimary(transform);
30 | var contents = await primary.readAsString();
31 |
32 | // Get all of the included inputs.
33 | var outputs = await Future.wait(contents.split(",").map((path) {
34 | var id;
35 | if (path.contains("|")) {
36 | id = new AssetId.parse(path);
37 | } else {
38 | id = new AssetId(transform.primaryInput.id.package, path);
39 | }
40 | return getInput(transform, id).then((input) => input.readAsString());
41 | }));
42 |
43 | var id = transform.primaryInput.id.changeExtension(".out");
44 | transform.addOutput(new Asset.fromString(id, outputs.join()));
45 | }
46 |
47 | String toString() => "many->1 $extension";
48 | }
49 |
--------------------------------------------------------------------------------
/test/cancelable_future_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.cancelable_future_test;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/src/utils.dart';
10 | import 'package:barback/src/utils/cancelable_future.dart';
11 | import 'package:unittest/unittest.dart';
12 |
13 | import 'utils.dart';
14 |
15 | main() {
16 | initConfig();
17 |
18 | var completer;
19 | var future;
20 | setUp(() {
21 | completer = new Completer();
22 | future = new CancelableFuture(completer.future);
23 | });
24 |
25 | group("when not canceled", () {
26 | test("correctly completes successfully", () {
27 | expect(future, completion(equals("success")));
28 | completer.complete("success");
29 | });
30 |
31 | test("correctly completes with an error", () {
32 | expect(future, throwsA(equals("error")));
33 | completer.completeError("error");
34 | });
35 | });
36 |
37 | group("when canceled", () {
38 | test("never completes successfully", () {
39 | var completed = false;
40 | future.whenComplete(() {
41 | completed = true;
42 | });
43 |
44 | future.cancel();
45 | completer.complete("success");
46 |
47 | expect(pumpEventQueue().then((_) => completed), completion(isFalse));
48 | });
49 |
50 | test("never completes with an error", () {
51 | var completed = false;
52 | future.catchError((_) {}).whenComplete(() {
53 | completed = true;
54 | });
55 |
56 | future.cancel();
57 | completer.completeError("error");
58 |
59 | expect(pumpEventQueue().then((_) => completed), completion(isFalse));
60 | });
61 | });
62 | }
63 |
--------------------------------------------------------------------------------
/test/transformer/aggregate_many_to_one.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer.aggregate_many_to_one;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 | import 'package:path/path.dart' as path;
11 |
12 | import 'mock_aggregate.dart';
13 |
14 | /// An [AggregateTransformer] that applies to all assets with a given extension.
15 | /// For each directory containing any of these assets, it produces an output
16 | /// file that contains the concatenation of all matched assets in that
17 | /// directory in alphabetic order by name.
18 | class AggregateManyToOneTransformer extends MockAggregateTransformer {
19 | /// The extension of assets to combine.
20 | final String extension;
21 |
22 | /// The basename of the output asset.
23 | ///
24 | /// The output asset's path will contain the directory name of the inputs as
25 | /// well.
26 | final String output;
27 |
28 | AggregateManyToOneTransformer(this.extension, this.output);
29 |
30 | String doClassifyPrimary(AssetId id) {
31 | if (id.extension != ".$extension") return null;
32 | return path.url.dirname(id.path);
33 | }
34 |
35 | Future doApply(AggregateTransform transform) async {
36 | var assets = await getPrimaryInputs(transform).toList();
37 | assets.sort((asset1, asset2) => asset1.id.path.compareTo(asset2.id.path));
38 | var contents =
39 | await Future.wait(assets.map((asset) => asset.readAsString()));
40 | var id =
41 | new AssetId(transform.package, path.url.join(transform.key, output));
42 | transform.addOutput(new Asset.fromString(id, contents.join('\n')));
43 | }
44 |
45 | String toString() => "aggregate $extension->$output";
46 | }
47 |
--------------------------------------------------------------------------------
/example/aggregate_transformer/lib/transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | import 'package:barback/barback.dart';
6 | import 'package:path/path.dart' as p;
7 |
8 | import 'dart:async';
9 |
10 | class MakeBook extends AggregateTransformer {
11 | // All transformers need to implement "asPlugin" to let Pub know that they
12 | // are transformers.
13 | MakeBook.asPlugin();
14 |
15 | // Implement the classifyPrimary method to claim any assets that you want
16 | // to handle. Return a value for the assets you want to handle,
17 | // or null for those that you do not want to handle.
18 | classifyPrimary(AssetId id) {
19 | // Only process assets where the filename ends with "recipe.html".
20 | if (!id.path.endsWith('recipe.html')) return null;
21 |
22 | // Return the path string, minus the recipe itself.
23 | // This is where the output asset will be written.
24 | return p.url.dirname(id.path);
25 | }
26 |
27 | // Implement the apply method to process the assets and create the
28 | // output asset.
29 | Future apply(AggregateTransform transform) async {
30 | var buffer = new StringBuffer()..write('');
31 |
32 | var assets = await transform.primaryInputs.toList();
33 | assets.sort((x, y) => x.id.compareTo(y.id));
34 | for (var asset in assets) {
35 | var content = await asset.readAsString();
36 | buffer.write(content);
37 | buffer.write('');
38 | }
39 | buffer.write('');
40 | // Write the output back to the same directory,
41 | // in a file named recipes.html.
42 | var id = new AssetId(
43 | transform.package, p.url.join(transform.key, "recipes.html"));
44 | transform.addOutput(new Asset.fromString(id, buffer.toString()));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/too_many_open_files_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.too_many_open_files_test;
6 |
7 | import 'dart:async';
8 | import 'dart:io';
9 |
10 | import 'package:barback/barback.dart';
11 | import 'package:path/path.dart' as pathos;
12 | import 'package:unittest/unittest.dart';
13 |
14 | import 'utils.dart';
15 |
16 | main() {
17 | initConfig();
18 |
19 | test("handles many simultaneous asset read() calls", () {
20 | runOnManyFiles((asset) => asset.read().toList());
21 | });
22 |
23 | test("handles many simultaneous asset readToString() calls", () {
24 | runOnManyFiles((asset) => asset.readAsString());
25 | });
26 | }
27 |
28 | runOnManyFiles(Future assetHandler(Asset asset)) {
29 | // Make a text file in a temp directory.
30 | var tempDir = Directory.systemTemp.createTempSync("barback").path;
31 | var filePath = pathos.join(tempDir, "out.txt");
32 |
33 | // Make sure it's large enough to not be read in a single chunk.
34 | var contents = new StringBuffer();
35 | for (var i = 0; i < 1024; i++) {
36 | contents.write(
37 | "this is a sixty four character long string that describes itself");
38 | }
39 |
40 | new File(filePath).writeAsStringSync(contents.toString());
41 |
42 | var id = new AssetId("myapp", "out.txt");
43 |
44 | // Create a large number of assets, larger than the file descriptor limit
45 | // of most machines and start reading from all of them.
46 | var futures = [];
47 | for (var i = 0; i < 1000; i++) {
48 | var asset = new Asset.fromPath(id, filePath);
49 | futures.add(assetHandler(asset));
50 | }
51 |
52 | expect(
53 | Future.wait(futures).whenComplete(() {
54 | new Directory(tempDir).delete(recursive: true);
55 | }),
56 | completes);
57 | }
58 |
--------------------------------------------------------------------------------
/lib/src/package_provider.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.package_provider;
6 |
7 | import 'dart:async';
8 |
9 | import 'asset/asset.dart';
10 | import 'asset/asset_id.dart';
11 |
12 | /// API for locating and accessing packages on disk.
13 | ///
14 | /// Implemented by pub and provided to barback so that it isn't coupled
15 | /// directly to pub.
16 | abstract class PackageProvider {
17 | /// The names of all packages that can be provided by this provider.
18 | ///
19 | /// This is equal to the transitive closure of the entrypoint package
20 | /// dependencies.
21 | Iterable get packages;
22 |
23 | /// Loads an asset from disk.
24 | ///
25 | /// This should be re-entrant; it may be called multiple times with the same
26 | /// id before the previously returned future has completed.
27 | ///
28 | /// If no asset with [id] exists, the provider should throw an
29 | /// [AssetNotFoundException].
30 | Future getAsset(AssetId id);
31 | }
32 |
33 | /// A PackageProvider for which some packages are known to be static—that is,
34 | /// the package has no transformers and its assets won't ever change.
35 | ///
36 | /// For static packages, rather than telling barback up-front which assets that
37 | /// package contains via [Barback.updateSources], barback will lazily query the
38 | /// provider for an asset when it's needed. This is much more efficient.
39 | abstract class StaticPackageProvider implements PackageProvider {
40 | /// The names of all static packages provided by this provider.
41 | ///
42 | /// This must be disjoint from [packages].
43 | Iterable get staticPackages;
44 |
45 | /// Returns all ids of assets in [package].
46 | ///
47 | /// This is used for [Barback.getAllAssets].
48 | Stream getAllAssetIds(String package);
49 | }
50 |
--------------------------------------------------------------------------------
/test/logger_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.logger_test;
6 |
7 | import 'package:barback/barback.dart';
8 | import 'package:scheduled_test/scheduled_test.dart';
9 |
10 | import 'utils.dart';
11 | import 'transformer/log.dart';
12 |
13 | main() {
14 | initConfig();
15 |
16 | test("logs messages from a transformer", () {
17 | var transformer = new LogTransformer([
18 | "error: This is an error.",
19 | "warning: This is a warning.",
20 | "info: This is info.",
21 | "fine: This is fine."
22 | ]);
23 | initGraph([
24 | "app|foo.txt"
25 | ], {
26 | "app": [
27 | [transformer]
28 | ]
29 | });
30 |
31 | updateSources(["app|foo.txt"]);
32 | buildShouldLog(LogLevel.ERROR, equals("This is an error."));
33 | buildShouldLog(LogLevel.WARNING, equals("This is a warning."));
34 | buildShouldLog(LogLevel.INFO, equals("This is info."));
35 | buildShouldLog(LogLevel.FINE, equals("This is fine."));
36 | });
37 |
38 | test("logs messages from a transformer group", () {
39 | var transformer = new LogTransformer([
40 | "error: This is an error.",
41 | "warning: This is a warning.",
42 | "info: This is info.",
43 | "fine: This is fine."
44 | ]);
45 |
46 | initGraph([
47 | "app|foo.txt"
48 | ], {
49 | "app": [
50 | [
51 | new TransformerGroup([
52 | [transformer]
53 | ])
54 | ]
55 | ]
56 | });
57 |
58 | updateSources(["app|foo.txt"]);
59 | buildShouldLog(LogLevel.ERROR, equals("This is an error."));
60 | buildShouldLog(LogLevel.WARNING, equals("This is a warning."));
61 | buildShouldLog(LogLevel.INFO, equals("This is info."));
62 | buildShouldLog(LogLevel.FINE, equals("This is fine."));
63 | });
64 | }
65 |
--------------------------------------------------------------------------------
/lib/src/utils/multiset.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.utils.multiset;
6 |
7 | import 'dart:collection';
8 |
9 | /// A set of objects where each object can appear multiple times.
10 | ///
11 | /// Like a set, this has amortized O(1) insertion, removal, and
12 | /// existence-checking of elements. Counting the number of copies of an element
13 | /// in the set is also amortized O(1).
14 | ///
15 | /// Distinct elements retain insertion order. Additional copies of an element
16 | /// beyond the first are grouped with the original element.
17 | ///
18 | /// If multiple equal elements are added, only the first actual object is
19 | /// retained.
20 | class Multiset extends IterableBase {
21 | /// A map from each element in the set to the number of copies of that element
22 | /// in the set.
23 | final _map = new Map();
24 |
25 | Iterator get iterator {
26 | return _map.keys
27 | .expand((element) =>
28 | new Iterable.generate(_map[element], (_) => element))
29 | .iterator;
30 | }
31 |
32 | Multiset() : super();
33 |
34 | /// Creates a multi-set and initializes it using the contents of [other].
35 | Multiset.from(Iterable other) : super() {
36 | other.forEach(add);
37 | }
38 |
39 | /// Adds [value] to the set.
40 | void add(E value) {
41 | _map.putIfAbsent(value, () => 0);
42 | _map[value] += 1;
43 | }
44 |
45 | /// Removes one copy of [value] from the set.
46 | ///
47 | /// Returns whether a copy of [value] was removed, regardless of whether more
48 | /// copies remain.
49 | bool remove(E value) {
50 | if (!_map.containsKey(value)) return false;
51 |
52 | _map[value] -= 1;
53 | if (_map[value] == 0) _map.remove(value);
54 | return true;
55 | }
56 |
57 | /// Returns whether [value] is in the set.
58 | bool contains(Object value) => _map.containsKey(value);
59 |
60 | /// Returns the number of copies of [value] in the set.
61 | int count(E value) => _map.containsKey(value) ? _map[value] : 0;
62 | }
63 |
--------------------------------------------------------------------------------
/test/transformer_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.transformer_test;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 | import 'package:unittest/unittest.dart';
11 |
12 | import 'utils.dart';
13 |
14 | main() {
15 | initConfig();
16 |
17 | group("isPrimary", () {
18 | test("defaults to allowedExtensions", () {
19 | var transformer = new ExtensionTransformer(".txt .bin");
20 | expect(transformer.isPrimary(new AssetId("pkg", "foo.txt")), isTrue);
21 |
22 | expect(transformer.isPrimary(new AssetId("pkg", "foo.bin")), isTrue);
23 |
24 | expect(transformer.isPrimary(new AssetId("pkg", "foo.nottxt")), isFalse);
25 | });
26 |
27 | test("supports multi-level extensions with allowedExtensions", () {
28 | var transformer = new ExtensionTransformer(".dart.js");
29 | expect(transformer.isPrimary(new AssetId("pkg", "foo.dart.js")), isTrue);
30 |
31 | expect(transformer.isPrimary(new AssetId("pkg", "foo.js")), isFalse);
32 |
33 | expect(transformer.isPrimary(new AssetId("pkg", "foo.dart")), isFalse);
34 | });
35 |
36 | test("throws an error for extensions without periods", () {
37 | expect(() => new ExtensionTransformer("dart"), throwsFormatException);
38 | });
39 |
40 | test("allows all files if allowedExtensions is not overridden", () {
41 | var transformer = new MockTransformer();
42 | expect(transformer.isPrimary(new AssetId("pkg", "foo.txt")), isTrue);
43 |
44 | expect(transformer.isPrimary(new AssetId("pkg", "foo.bin")), isTrue);
45 |
46 | expect(transformer.isPrimary(new AssetId("pkg", "anything")), isTrue);
47 | });
48 | });
49 | }
50 |
51 | class MockTransformer extends Transformer {
52 | MockTransformer();
53 |
54 | Future apply(Transform transform) => new Future.value();
55 | }
56 |
57 | class ExtensionTransformer extends Transformer {
58 | final String allowedExtensions;
59 |
60 | ExtensionTransformer(this.allowedExtensions);
61 |
62 | Future apply(Transform transform) => new Future.value();
63 | }
64 |
--------------------------------------------------------------------------------
/lib/src/transformer/barback_settings.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.barback_settings;
6 |
7 | /// A generic settings object for providing configuration details to
8 | /// [Transformer]s.
9 | ///
10 | /// Barback does not specify *how* this is provided to transformers. It is up
11 | /// to a host application to handle this. (For example, pub passes this to the
12 | /// transformer's constructor.)
13 | class BarbackSettings {
14 | /// An open-ended map of configuration properties specific to this
15 | /// transformer.
16 | ///
17 | /// The contents of the map should be serializable across isolates, but
18 | /// otherwise can contain whatever you want.
19 | final Map configuration;
20 |
21 | /// The mode that user is running Barback in.
22 | ///
23 | /// This will be the same for all transformers in a running instance of
24 | /// Barback.
25 | final BarbackMode mode;
26 |
27 | BarbackSettings(this.configuration, this.mode);
28 | }
29 |
30 | /// Enum-like class for specifying a mode that transformers may be run in.
31 | ///
32 | /// Note that this is not a *closed* set of enum values. Host applications may
33 | /// define their own values for this, so a transformer relying on it should
34 | /// ensure that it behaves sanely with unknown values.
35 | class BarbackMode {
36 | /// The normal mode used during development.
37 | static const DEBUG = const BarbackMode._("debug");
38 |
39 | /// The normal mode used to build an application for deploying to production.
40 | static const RELEASE = const BarbackMode._("release");
41 |
42 | /// The name of the mode.
43 | ///
44 | /// By convention, this is a lowercase string.
45 | final String name;
46 |
47 | /// Create a mode named [name].
48 | factory BarbackMode(String name) {
49 | // Use canonical instances of known names.
50 | switch (name) {
51 | case "debug":
52 | return BarbackMode.DEBUG;
53 | case "release":
54 | return BarbackMode.RELEASE;
55 | default:
56 | return new BarbackMode._(name);
57 | }
58 | }
59 |
60 | const BarbackMode._(this.name);
61 |
62 | String toString() => name;
63 | }
64 |
--------------------------------------------------------------------------------
/lib/src/asset/asset_set.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.asset.asset_set;
6 |
7 | import 'dart:collection';
8 |
9 | import 'asset.dart';
10 | import 'asset_id.dart';
11 |
12 | /// A set of [Asset]s with distinct IDs.
13 | ///
14 | /// This uses the [AssetId] of each asset to determine uniqueness, so no two
15 | /// assets with the same ID can be in the set.
16 | class AssetSet extends IterableBase {
17 | final _assets = new Map();
18 |
19 | /// The ids of the assets in the set.
20 | Iterable get ids => _assets.keys;
21 |
22 | AssetSet();
23 |
24 | /// Creates a new AssetSet from the contents of [other].
25 | ///
26 | /// If multiple assets in [other] have the same id, the last one takes
27 | /// precedence.
28 | AssetSet.from(Iterable other) {
29 | for (var asset in other) {
30 | _assets[asset.id] = asset;
31 | }
32 | }
33 |
34 | Iterator get iterator => _assets.values.iterator;
35 |
36 | int get length => _assets.length;
37 |
38 | /// Gets the [Asset] in the set with [id], or returns `null` if no asset with
39 | /// that ID is present.
40 | Asset operator [](AssetId id) => _assets[id];
41 |
42 | /// Adds [asset] to the set.
43 | ///
44 | /// If there is already an asset with that ID in the set, it is replaced by
45 | /// the new one. Returns [asset].
46 | Asset add(Asset asset) {
47 | _assets[asset.id] = asset;
48 | return asset;
49 | }
50 |
51 | /// Adds [assets] to the set.
52 | void addAll(Iterable assets) {
53 | assets.forEach(add);
54 | }
55 |
56 | /// Returns `true` if the set contains [asset].
57 | bool contains(Object asset) => asset is Asset && _assets[asset.id] == asset;
58 |
59 | /// Returns `true` if the set contains an [Asset] with [id].
60 | bool containsId(AssetId id) {
61 | return _assets.containsKey(id);
62 | }
63 |
64 | /// If the set contains an [Asset] with [id], removes and returns it.
65 | Asset removeId(AssetId id) => _assets.remove(id);
66 |
67 | /// Removes all assets from the set.
68 | void clear() {
69 | _assets.clear();
70 | }
71 |
72 | String toString() => _assets.toString();
73 | }
74 |
--------------------------------------------------------------------------------
/lib/src/graph/node_status.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.graph.node_status;
6 |
7 | /// The status of a node in barback's package graph.
8 | ///
9 | /// A node has three possible statuses: [IDLE], [MATERIALIZING], and [RUNNING].
10 | /// These are ordered from least dirty to most dirty; the [dirtier] and
11 | /// [dirtiest] functions make use of this ordering.
12 | class NodeStatus {
13 | /// The node has finished its work and won't do anything else until external
14 | /// input causes it to.
15 | ///
16 | /// For deferred nodes, this may indicate that they're finished declaring
17 | /// their outputs and waiting to be forced.
18 | static const IDLE = const NodeStatus("idle");
19 |
20 | /// The node has declared its outputs but their concrete values are still
21 | /// being generated.
22 | ///
23 | /// This is only meaningful for nodes that are or contain declaring
24 | /// transformers. Note that a lazy transformer that's declared its outputs but
25 | /// isn't actively working to generate them is considered [IDLE], not
26 | /// [MATERIALIZING].
27 | static const MATERIALIZING = const NodeStatus("materializing");
28 |
29 | /// The node is actively working on declaring or generating its outputs.
30 | ///
31 | /// Declaring transformers are only considered dirty until they're finished
32 | /// declaring their outputs; past that point, they're always either
33 | /// [MATERIALIZING] or [IDLE]. Non-declaring transformers, by contrast, are
34 | /// always either [RUNNING] or [IDLE].
35 | static const RUNNING = const NodeStatus("running");
36 |
37 | final String _name;
38 |
39 | /// Returns the dirtiest status in [statuses].
40 | static NodeStatus dirtiest(Iterable statuses) => statuses.fold(
41 | NodeStatus.IDLE, (status1, status2) => status1.dirtier(status2));
42 |
43 | const NodeStatus(this._name);
44 |
45 | String toString() => _name;
46 |
47 | /// Returns [this] or [other], whichever is dirtier.
48 | NodeStatus dirtier(NodeStatus other) {
49 | if (this == RUNNING || other == RUNNING) return RUNNING;
50 | if (this == MATERIALIZING || other == MATERIALIZING) return MATERIALIZING;
51 | return IDLE;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/test/asset_id_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.asset_id_test;
6 |
7 | import 'package:barback/barback.dart';
8 | import 'package:unittest/unittest.dart';
9 |
10 | import 'utils.dart';
11 |
12 | main() {
13 | initConfig();
14 | group("constructor", () {
15 | test("normalizes the path", () {
16 | var id = new AssetId("app", r"path/././/to/drop/..//asset.txt");
17 | expect(id.path, equals("path/to/asset.txt"));
18 | });
19 |
20 | test("normalizes backslashes to slashes in the path", () {
21 | var id = new AssetId("app", r"path\to/asset.txt");
22 | expect(id.path, equals("path/to/asset.txt"));
23 | });
24 | });
25 |
26 | group("parse", () {
27 | test("parses the package and path", () {
28 | var id = new AssetId.parse("package|path/to/asset.txt");
29 | expect(id.package, equals("package"));
30 | expect(id.path, equals("path/to/asset.txt"));
31 | });
32 |
33 | test("throws if there are multiple '|'", () {
34 | expect(() => new AssetId.parse("app|path|wtf"), throwsFormatException);
35 | });
36 |
37 | test("throws if the package name is empty '|'", () {
38 | expect(() => new AssetId.parse("|asset.txt"), throwsFormatException);
39 | });
40 |
41 | test("throws if the path is empty '|'", () {
42 | expect(() => new AssetId.parse("app|"), throwsFormatException);
43 | });
44 |
45 | test("normalizes the path", () {
46 | var id = new AssetId.parse(r"app|path/././/to/drop/..//asset.txt");
47 | expect(id.path, equals("path/to/asset.txt"));
48 | });
49 |
50 | test("normalizes backslashes to slashes in the path", () {
51 | var id = new AssetId.parse(r"app|path\to/asset.txt");
52 | expect(id.path, equals("path/to/asset.txt"));
53 | });
54 | });
55 |
56 | test("equals another ID with the same package and path", () {
57 | expect(new AssetId.parse("foo|asset.txt"),
58 | equals(new AssetId.parse("foo|asset.txt")));
59 |
60 | expect(new AssetId.parse("foo|asset.txt"),
61 | isNot(equals(new AssetId.parse("bar|asset.txt"))));
62 |
63 | expect(new AssetId.parse("foo|asset.txt"),
64 | isNot(equals(new AssetId.parse("bar|other.txt"))));
65 | });
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/build_result.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.build_result;
6 |
7 | import 'errors.dart';
8 | import 'utils.dart';
9 |
10 | /// An event indicating that the cascade has finished building all assets.
11 | ///
12 | /// A build can end either in success or failure. If there were no errors during
13 | /// the build, it's considered to be a success; any errors render it a failure,
14 | /// although individual assets may still have built successfully.
15 | class BuildResult {
16 | // TODO(rnystrom): Revise how to track error results. Errors can come from
17 | // both logs and exceptions. Accumulating them is likely slow and a waste of
18 | // memory. If we do want to accumulate them, we should at least unify them
19 | // in a single collection (probably of log entries).
20 | /// All errors that were thrown during the build.
21 | final Set errors;
22 |
23 | /// `true` if the build succeeded.
24 | bool get succeeded => errors.isEmpty;
25 |
26 | BuildResult(Iterable errors)
27 | : errors = flattenAggregateExceptions(errors).toSet();
28 |
29 | /// Creates a build result indicating a successful build.
30 | ///
31 | /// This equivalent to a build result with no errors.
32 | BuildResult.success() : this([]);
33 |
34 | /// Creates a single [BuildResult] that contains all of the errors of
35 | /// [results].
36 | factory BuildResult.aggregate(Iterable results) {
37 | var errors = unionAll(results.map((result) => result.errors));
38 | return new BuildResult(errors);
39 | }
40 |
41 | String toString() {
42 | if (succeeded) return "success";
43 |
44 | return "errors:\n" +
45 | errors.map((error) {
46 | var stackTrace = null;
47 | if (error is TransformerException) {
48 | stackTrace = error.stackTrace.terse;
49 | } else if (error is AssetLoadException) {
50 | stackTrace = error.stackTrace.terse;
51 | }
52 |
53 | var msg = new StringBuffer();
54 | msg.write(prefixLines(error.toString()));
55 | if (stackTrace != null) {
56 | msg.write("\n\n");
57 | msg.write("Stack chain:\n");
58 | msg.write(prefixLines(stackTrace.toString()));
59 | }
60 | return msg.toString();
61 | }).join("\n\n");
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/src/utils/file_pool.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.utils.file_pool;
6 |
7 | import 'dart:async';
8 | import 'dart:convert';
9 | import 'dart:io';
10 |
11 | import 'package:pool/pool.dart';
12 |
13 | import '../utils.dart';
14 |
15 | /// Manages a pool of files that are opened for reading to cope with maximum
16 | /// file descriptor limits.
17 | ///
18 | /// If a file cannot be opened because too many files are already open, this
19 | /// will defer the open until a previously opened file is closed and then try
20 | /// again. If this doesn't succeed after a certain amount of time, the open
21 | /// will fail and the original "too many files" exception will be thrown.
22 | class FilePool {
23 | /// The underlying pool.
24 | ///
25 | /// The maximum number of allocated descriptors is based on empirical tests
26 | /// that indicate that beyond 32, additional file reads don't provide
27 | /// substantial additional throughput.
28 | final Pool _pool = new Pool(32, timeout: new Duration(seconds: 60));
29 |
30 | /// Opens the file at [path] for reading.
31 | ///
32 | /// When the returned stream is listened to, if there are too many files
33 | /// open, this will wait for a previously opened file to be closed and then
34 | /// try again.
35 | Stream> openRead(String path) {
36 | return futureStream(_pool.request().then((resource) {
37 | return new File(path)
38 | .openRead()
39 | .transform(new StreamTransformer.fromHandlers(handleDone: (sink) {
40 | sink.close();
41 | resource.release();
42 | }));
43 | }));
44 | }
45 |
46 | /// Reads [path] as a string using [encoding].
47 | ///
48 | /// If there are too many files open and the read fails, this will wait for
49 | /// a previously opened file to be closed and then try again.
50 | Future readAsString(String path, Encoding encoding) {
51 | return _readAsBytes(path).then(encoding.decode);
52 | }
53 |
54 | /// Reads [path] as a list of bytes, using [openRead] to retry if there are
55 | /// failures.
56 | Future> _readAsBytes(String path) {
57 | var completer = new Completer>();
58 | var builder = new BytesBuilder();
59 |
60 | openRead(path).listen(builder.add, onDone: () {
61 | completer.complete(builder.takeBytes());
62 | }, onError: completer.completeError, cancelOnError: true);
63 |
64 | return completer.future;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/graph/node_streams.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.graph.node_streams;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_node.dart';
10 | import '../log.dart';
11 | import '../utils/stream_pool.dart';
12 | import 'node_status.dart';
13 |
14 | /// A collection of streams that are common to nodes in barback's package graph.
15 | class NodeStreams {
16 | /// A stream that emits an event every time the node's status changes.
17 | ///
18 | /// This will emit the new status. It's guaranteed to emit an event only when
19 | /// the status changes from the previous value. To ensure this, callers should
20 | /// emit status changes using [changeStatus]. The initial status is assumed to
21 | /// be [NodeStatus.RUNNING].
22 | Stream get onStatusChange => _onStatusChangeController.stream;
23 | final _onStatusChangeController =
24 | new StreamController.broadcast(sync: true);
25 |
26 | /// A stream that emits any new assets produced by the node.
27 | ///
28 | /// Assets are emitted synchronously to ensure that any changes are thoroughly
29 | /// propagated as soon as they occur.
30 | Stream get onAsset => onAssetPool.stream;
31 | final onAssetPool = new StreamPool.broadcast();
32 | final onAssetController =
33 | new StreamController.broadcast(sync: true);
34 |
35 | /// A stream that emits an event whenever any the node logs an entry.
36 | Stream get onLog => onLogPool.stream;
37 | final onLogPool = new StreamPool.broadcast();
38 | final onLogController = new StreamController.broadcast(sync: true);
39 |
40 | var _previousStatus = NodeStatus.RUNNING;
41 |
42 | /// Whether [this] has been closed.
43 | bool get isClosed => onAssetController.isClosed;
44 |
45 | NodeStreams() {
46 | onAssetPool.add(onAssetController.stream);
47 | onLogPool.add(onLogController.stream);
48 | }
49 |
50 | /// Emits a status change notification via [onStatusChange].
51 | ///
52 | /// This guarantees that a change notification won't be emitted if the status
53 | /// didn't actually change.
54 | void changeStatus(NodeStatus status) {
55 | if (_previousStatus != status) _onStatusChangeController.add(status);
56 | }
57 |
58 | /// Closes all the streams.
59 | void close() {
60 | _onStatusChangeController.close();
61 | onAssetController.close();
62 | onAssetPool.close();
63 | onLogController.close();
64 | onLogPool.close();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/utils/stream_replayer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.utils.stream_replayer;
6 |
7 | import 'dart:async';
8 | import 'dart:collection';
9 |
10 | import '../utils.dart';
11 |
12 | /// Records the values and errors that are sent through a stream and allows them
13 | /// to be replayed arbitrarily many times.
14 | ///
15 | /// This only listens to the wrapped stream when a replayed stream gets a
16 | /// listener.
17 | class StreamReplayer {
18 | /// The wrapped stream.
19 | final Stream _stream;
20 |
21 | /// Whether or not [this] has started listening to [_stream].
22 | bool _isSubscribed = false;
23 |
24 | /// Whether or not [_stream] has been closed.
25 | bool _isClosed = false;
26 |
27 | /// The buffer of events or errors that have already been emitted by
28 | /// [_stream].
29 | ///
30 | /// Each element is a [Fallible] that's either a value or an error sent
31 | /// through the stream.
32 | final _buffer = new Queue>();
33 |
34 | /// The controllers that are listening for future events from [_stream].
35 | final _controllers = new Set>();
36 |
37 | StreamReplayer(this._stream);
38 |
39 | /// Returns a stream that replays the values and errors of the input stream.
40 | ///
41 | /// This stream is a buffered stream.
42 | Stream getReplay() {
43 | var controller = new StreamController(onListen: _subscribe);
44 |
45 | for (var eventOrError in _buffer) {
46 | if (eventOrError.hasValue) {
47 | controller.add(eventOrError.value);
48 | } else {
49 | controller.addError(eventOrError.error, eventOrError.stackTrace);
50 | }
51 | }
52 | if (_isClosed) {
53 | controller.close();
54 | } else {
55 | _controllers.add(controller);
56 | }
57 | return controller.stream;
58 | }
59 |
60 | /// Subscribe to [_stream] if we haven't yet done so.
61 | void _subscribe() {
62 | if (_isSubscribed || _isClosed) return;
63 | _isSubscribed = true;
64 |
65 | _stream.listen((data) {
66 | _buffer.add(new Fallible.withValue(data));
67 | for (var controller in _controllers) {
68 | controller.add(data);
69 | }
70 | }, onError: (error, [stackTrace]) {
71 | _buffer.add(new Fallible.withError(error, stackTrace));
72 | for (var controller in _controllers) {
73 | controller.addError(error, stackTrace);
74 | }
75 | }, onDone: () {
76 | _isClosed = true;
77 | for (var controller in _controllers) {
78 | controller.close();
79 | }
80 | _controllers.clear();
81 | });
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/lib/src/utils/stream_pool.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.utils.stream_pool;
6 |
7 | import 'dart:async';
8 |
9 | /// A pool of streams whose events are unified and emitted through a central
10 | /// stream.
11 | class StreamPool {
12 | /// The stream through which all events from streams in the pool are emitted.
13 | Stream get stream => _controller.stream;
14 | final StreamController _controller;
15 |
16 | /// Subscriptions to the streams that make up the pool.
17 | final _subscriptions = new Map, StreamSubscription>();
18 |
19 | /// Creates a new stream pool that only supports a single subscriber.
20 | ///
21 | /// Any events from broadcast streams in the pool will be buffered until a
22 | /// listener is subscribed.
23 | StreamPool()
24 | // Create the controller as sync so that any sync input streams will be
25 | // forwarded synchronously. Async input streams will have their asynchrony
26 | // preserved, since _controller.add will be called asynchronously.
27 | : _controller = new StreamController(sync: true);
28 |
29 | /// Creates a new stream pool where [stream] can be listened to more than
30 | /// once.
31 | ///
32 | /// Any events from buffered streams in the pool will be emitted immediately,
33 | /// regardless of whether [stream] has any subscribers.
34 | StreamPool.broadcast()
35 | // Create the controller as sync so that any sync input streams will be
36 | // forwarded synchronously. Async input streams will have their asynchrony
37 | // preserved, since _controller.add will be called asynchronously.
38 | : _controller = new StreamController.broadcast(sync: true);
39 |
40 | /// Adds [stream] as a member of this pool.
41 | ///
42 | /// Any events from [stream] will be emitted through [this.stream]. If
43 | /// [stream] is sync, they'll be emitted synchronously; if [stream] is async,
44 | /// they'll be emitted asynchronously.
45 | void add(Stream stream) {
46 | if (_subscriptions.containsKey(stream)) return;
47 | _subscriptions[stream] = stream.listen(_controller.add,
48 | onError: _controller.addError, onDone: () => remove(stream));
49 | }
50 |
51 | /// Removes [stream] as a member of this pool.
52 | void remove(Stream stream) {
53 | var subscription = _subscriptions.remove(stream);
54 | if (subscription != null) subscription.cancel();
55 | }
56 |
57 | /// Removes all streams from this pool and closes [stream].
58 | void close() {
59 | for (var subscription in _subscriptions.values) {
60 | subscription.cancel();
61 | }
62 | _subscriptions.clear();
63 | _controller.close();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/test/package_graph/repetition_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.package_graph.transform_test;
6 |
7 | import 'package:scheduled_test/scheduled_test.dart';
8 |
9 | import '../utils.dart';
10 |
11 | // This tests the behavior of barback under many operations happening in quick
12 | // succession. Since Barback is so asynchronous, it's easy for it to have subtle
13 | // dependencies on the commonly-used and -tested usage patterns. These tests
14 | // exist to stress-test less-common usage patterns in order to root out
15 | // additional bugs.
16 |
17 | main() {
18 | initConfig();
19 |
20 | test("updates sources many times", () {
21 | initGraph([
22 | "app|foo.txt"
23 | ], {
24 | "app": [
25 | [new RewriteTransformer("txt", "out")]
26 | ]
27 | });
28 |
29 | for (var i = 0; i < 1000; i++) {
30 | updateSources(["app|foo.txt"]);
31 | }
32 |
33 | expectAsset("app|foo.out", "foo.out");
34 | buildShouldSucceed();
35 | });
36 |
37 | test("updates and then removes sources many times", () {
38 | initGraph([
39 | "app|foo.txt"
40 | ], {
41 | "app": [
42 | [new RewriteTransformer("txt", "out")]
43 | ]
44 | });
45 |
46 | for (var i = 0; i < 1000; i++) {
47 | updateSources(["app|foo.txt"]);
48 | removeSources(["app|foo.txt"]);
49 | }
50 |
51 | expectNoAsset("app|foo.out");
52 | expectNoAsset("app|foo.txt");
53 | buildShouldSucceed();
54 | });
55 |
56 | test("updates transformers many times", () {
57 | var rewrite = new RewriteTransformer("txt", "out");
58 | initGraph([
59 | "app|foo.txt"
60 | ], {
61 | "app": [
62 | [rewrite]
63 | ]
64 | });
65 | updateSources(["app|foo.txt"]);
66 |
67 | for (var i = 0; i < 1000; i++) {
68 | updateTransformers("app", [
69 | [rewrite]
70 | ]);
71 | }
72 |
73 | expectAsset("app|foo.out", "foo.out");
74 | buildShouldSucceed();
75 | });
76 |
77 | test("updates and removes transformers many times", () {
78 | var rewrite = new RewriteTransformer("txt", "out");
79 | initGraph([
80 | "app|foo.txt"
81 | ], {
82 | "app": [
83 | [rewrite]
84 | ]
85 | });
86 | updateSources(["app|foo.txt"]);
87 |
88 | for (var i = 0; i < 1000; i++) {
89 | updateTransformers("app", [
90 | [rewrite]
91 | ]);
92 | updateTransformers("app", [[]]);
93 | }
94 |
95 | expectAsset("app|foo.txt", "foo");
96 | expectNoAsset("app|foo.out");
97 | buildShouldSucceed();
98 | });
99 | }
100 |
--------------------------------------------------------------------------------
/test/multiset_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.multiset_test;
6 |
7 | import 'package:barback/src/utils/multiset.dart';
8 | import 'package:unittest/unittest.dart';
9 |
10 | import 'utils.dart';
11 |
12 | main() {
13 | initConfig();
14 |
15 | test("new Multiset() creates an empty set", () {
16 | var multiSet = new Multiset();
17 | expect(multiSet, isEmpty);
18 | expect(multiSet.contains(1), isFalse);
19 | expect(multiSet.count(1), equals(0));
20 | });
21 |
22 | test("new Multiset.from(...) constructs a set from the argument", () {
23 | var multiSet = new Multiset.from([1, 2, 3, 2, 4]);
24 | expect(multiSet.toList(), equals([1, 2, 2, 3, 4]));
25 | expect(multiSet.contains(1), isTrue);
26 | expect(multiSet.contains(5), isFalse);
27 | expect(multiSet.count(1), equals(1));
28 | expect(multiSet.count(2), equals(2));
29 | expect(multiSet.count(5), equals(0));
30 | });
31 |
32 | test("an element can be added and removed once", () {
33 | var multiSet = new Multiset();
34 | expect(multiSet.contains(1), isFalse);
35 | multiSet.add(1);
36 | expect(multiSet.contains(1), isTrue);
37 | multiSet.remove(1);
38 | expect(multiSet.contains(1), isFalse);
39 | });
40 |
41 | test("a set can contain multiple copies of an element", () {
42 | var multiSet = new Multiset();
43 | expect(multiSet.count(1), equals(0));
44 | multiSet.add(1);
45 | expect(multiSet.count(1), equals(1));
46 | multiSet.add(1);
47 | expect(multiSet.count(1), equals(2));
48 | multiSet.remove(1);
49 | expect(multiSet.count(1), equals(1));
50 | multiSet.remove(1);
51 | expect(multiSet.count(1), equals(0));
52 | });
53 |
54 | test("remove returns false if the element wasn't in the set", () {
55 | var multiSet = new Multiset();
56 | expect(multiSet.remove(1), isFalse);
57 | });
58 |
59 | test("remove returns true if the element was in the set", () {
60 | var multiSet = new Multiset.from([1]);
61 | expect(multiSet.remove(1), isTrue);
62 | });
63 |
64 | test(
65 | "remove returns true if the element was in the set even if more copies "
66 | "remain", () {
67 | var multiSet = new Multiset.from([1, 1, 1]);
68 | expect(multiSet.remove(1), isTrue);
69 | });
70 |
71 | test("iterator orders distinct elements in insertion order", () {
72 | var multiSet = new Multiset()..add(1)..add(2)..add(3)..add(4)..add(5);
73 | expect(multiSet.toList(), equals([1, 2, 3, 4, 5]));
74 | });
75 |
76 | test("iterator groups multiple copies of an element together", () {
77 | var multiSet = new Multiset()..add(1)..add(2)..add(1)..add(2)..add(1);
78 | expect(multiSet.toList(), equals([1, 1, 1, 2, 2]));
79 | });
80 | }
81 |
--------------------------------------------------------------------------------
/lib/src/transformer/aggregate_transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.aggregate_transformer;
6 |
7 | import '../asset/asset_id.dart';
8 | import 'aggregate_transform.dart';
9 |
10 | /// An alternate interface for transformers that want to perform aggregate
11 | /// transformations on multiple inputs without any individual one of them being
12 | /// considered "primary".
13 | ///
14 | /// This is useful for transformers like image spriting, where all the images in
15 | /// a directory need to be combined into a single image. A normal [Transformer]
16 | /// can't do this gracefully since when it's running on a single image, it has
17 | /// no way of knowing what other images exist to request as secondary inputs.
18 | ///
19 | /// Aggregate transformers work by classifying assets into different groups
20 | /// based on their ids in [classifyPrimary]. Then [apply] is run once for each
21 | /// group. For example, a spriting transformer might put each image asset into a
22 | /// group identified by its directory name. All images in a given directory will
23 | /// end up in the same group, and they'll all be passed to one [apply] call.
24 | ///
25 | /// If possible, aggregate transformers should implement
26 | /// [DeclaringAggregateTransformer] as well to help barback optimize the package
27 | /// graph.
28 | abstract class AggregateTransformer {
29 | /// Classifies an asset id by returning a key identifying which group the
30 | /// asset should be placed in.
31 | ///
32 | /// All assets for which [classifyPrimary] returns the same key are passed
33 | /// together to the same [apply] call.
34 | ///
35 | /// This may return [Future] or, if it's entirely synchronous,
36 | /// [String]. Any string can be used to classify an asset. If possible,
37 | /// though, this should return a path-like string to aid in logging.
38 | ///
39 | /// A return value of `null` indicates that the transformer is not interested
40 | /// in an asset. Assets with a key of `null` will not be passed to any [apply]
41 | /// call; this is equivalent to [Transformer.isPrimary] returning `false`.
42 | classifyPrimary(AssetId id);
43 |
44 | /// Runs this transformer on a group of primary inputs specified by
45 | /// [transform].
46 | ///
47 | /// If this does asynchronous work, it should return a [Future] that completes
48 | /// once it's finished.
49 | ///
50 | /// This may complete before [AggregateTransform.primarInputs] is closed. For
51 | /// example, it may know that each key will only have two inputs associated
52 | /// with it, and so use `transform.primaryInputs.take(2)` to access only those
53 | /// inputs.
54 | apply(AggregateTransform transform);
55 |
56 | String toString() => runtimeType.toString().replaceAll("Transformer", "");
57 | }
58 |
--------------------------------------------------------------------------------
/lib/src/transformer/declaring_transform.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.declaring_transform;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_id.dart';
10 | import 'declaring_aggregate_transform.dart';
11 | import 'transform_logger.dart';
12 |
13 | /// Creates a new [DeclaringTransform] wrapping an
14 | /// [AggregateDeclaringTransform].
15 | ///
16 | /// Although barback internally works in terms of
17 | /// [DeclaringAggregateTransformer]s, most transformers only work on individual
18 | /// primary inputs in isolation. We want to allow those transformers to
19 | /// implement the more user-friendly [DeclaringTransformer] interface which
20 | /// takes the more user-friendly [DeclaringTransform] object. This method wraps
21 | /// the more general [DeclaringAggregateTransform] to return a
22 | /// [DeclaringTransform] instead.
23 | Future newDeclaringTransform(
24 | DeclaringAggregateTransform aggregate) {
25 | // A wrapped [Transformer] will assign each primary input a unique transform
26 | // key, so we can safely get the first asset emitted. We don't want to wait
27 | // for the stream to close, since that requires barback to prove that no more
28 | // new assets will be generated.
29 | return aggregate.primaryIds.first
30 | .then((primaryId) => new DeclaringTransform._(aggregate, primaryId));
31 | }
32 |
33 | /// A transform for [DeclaringTransformer]s that allows them to declare the ids
34 | /// of the outputs they'll generate without generating the concrete bodies of
35 | /// those outputs.
36 | class DeclaringTransform {
37 | /// The underlying aggregate transform.
38 | final DeclaringAggregateTransform _aggregate;
39 |
40 | final AssetId primaryId;
41 |
42 | /// A logger so that the [Transformer] can report build details.
43 | TransformLogger get logger => _aggregate.logger;
44 |
45 | DeclaringTransform._(this._aggregate, this.primaryId);
46 |
47 | /// Stores [id] as the id of an output that will be created by this
48 | /// transformation when it's run.
49 | ///
50 | /// A transformation can declare as many assets as it wants. If
51 | /// [DeclaringTransformer.declareOutputs] declareds a given asset id for a
52 | /// given input, [Transformer.apply] should emit the corresponding asset as
53 | /// well.
54 | void declareOutput(AssetId id) => _aggregate.declareOutput(id);
55 |
56 | /// Consume the primary input so that it doesn't get processed by future
57 | /// phases or emitted once processing has finished.
58 | ///
59 | /// Normally the primary input will automatically be forwarded unless the
60 | /// transformer overwrites it by emitting an input with the same id. This
61 | /// allows the transformer to tell barback not to forward the primary input
62 | /// even if it's not overwritten.
63 | void consumePrimary() => _aggregate.consumePrimary(primaryId);
64 | }
65 |
--------------------------------------------------------------------------------
/lib/src/graph/group_runner.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.graph.group_runner;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_node.dart';
10 | import '../log.dart';
11 | import '../transformer/transformer_group.dart';
12 | import '../utils/stream_pool.dart';
13 | import 'node_status.dart';
14 | import 'phase.dart';
15 |
16 | /// A class that processes all of the phases in a single transformer group.
17 | ///
18 | /// A group takes many inputs, processes them, and emits many outputs.
19 | class GroupRunner {
20 | /// The group this runner runs.
21 | final TransformerGroup _group;
22 |
23 | /// A string describing the location of [this] in the transformer graph.
24 | final String _location;
25 |
26 | /// The phases defined by this group.
27 | final _phases = new List();
28 |
29 | /// How far along [this] is in processing its assets.
30 | NodeStatus get status {
31 | // Just check the last phase, since it will check all the previous phases
32 | // itself.
33 | return _phases.last.status;
34 | }
35 |
36 | /// A stream that emits an event every time the group's status changes.
37 | Stream get onStatusChange => _onStatusChange;
38 | Stream _onStatusChange;
39 |
40 | /// A stream that emits any new assets emitted by [this].
41 | ///
42 | /// Assets are emitted synchronously to ensure that any changes are thoroughly
43 | /// propagated as soon as they occur.
44 | Stream get onAsset => _onAsset;
45 | Stream _onAsset;
46 |
47 | /// A stream that emits an event whenever any transforms in this group logs
48 | /// an entry.
49 | Stream get onLog => _onLogPool.stream;
50 | final _onLogPool = new StreamPool.broadcast();
51 |
52 | GroupRunner(Phase previous, this._group, this._location) {
53 | _addPhase(previous.addPhase(_location), []);
54 | for (var phase in _group.phases) {
55 | _addPhase(_phases.last.addPhase(), phase);
56 | }
57 |
58 | _onAsset = _phases.last.onAsset;
59 | _onStatusChange = _phases.last.onStatusChange;
60 | }
61 |
62 | /// Add a phase with [contents] to [this]'s list of phases.
63 | ///
64 | /// [contents] should be an inner [Iterable] from a [TransformGroup.phases]
65 | /// value.
66 | void _addPhase(Phase phase, Iterable contents) {
67 | _phases.add(phase);
68 | _onLogPool.add(phase.onLog);
69 | phase.updateTransformers(contents);
70 | }
71 |
72 | /// Force all [LazyTransformer]s' transforms in this group to begin producing
73 | /// concrete assets.
74 | void forceAllTransforms() {
75 | for (var phase in _phases) {
76 | phase.forceAllTransforms();
77 | }
78 | }
79 |
80 | /// Removes this group and all sub-phases within it.
81 | void remove() {
82 | _onLogPool.close();
83 | for (var phase in _phases) {
84 | phase.remove();
85 | }
86 | }
87 |
88 | String toString() => "group in phase $_location for $_group";
89 | }
90 |
--------------------------------------------------------------------------------
/lib/src/transformer/transform_logger.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.transform_logger;
6 |
7 | import 'package:source_span/source_span.dart';
8 |
9 | import '../asset/asset_id.dart';
10 | import '../log.dart';
11 |
12 | typedef void LogFunction(
13 | AssetId asset, LogLevel level, String message, SourceSpan span);
14 |
15 | /// Object used to report warnings and errors encountered while running a
16 | /// transformer.
17 | class TransformLogger {
18 | final LogFunction _logFunction;
19 |
20 | TransformLogger(this._logFunction);
21 |
22 | /// Logs an informative message.
23 | ///
24 | /// If [asset] is provided, the log entry is associated with that asset.
25 | /// Otherwise it's associated with the primary input of [transformer]. If
26 | /// present, [span] indicates the location in the input asset that caused the
27 | /// error.
28 | void info(String message, {AssetId asset, SourceSpan span}) {
29 | _logFunction(asset, LogLevel.INFO, message, span);
30 | }
31 |
32 | /// Logs a message that won't be displayed unless the user is running in
33 | /// verbose mode.
34 | ///
35 | /// If [asset] is provided, the log entry is associated with that asset.
36 | /// Otherwise it's associated with the primary input of [transformer]. If
37 | /// present, [span] indicates the location in the input asset that caused the
38 | /// error.
39 | void fine(String message, {AssetId asset, SourceSpan span}) {
40 | _logFunction(asset, LogLevel.FINE, message, span);
41 | }
42 |
43 | /// Logs a warning message.
44 | ///
45 | /// If [asset] is provided, the log entry is associated with that asset.
46 | /// Otherwise it's associated with the primary input of [transformer]. If
47 | /// present, [span] indicates the location in the input asset that caused the
48 | /// error.
49 | void warning(String message, {AssetId asset, SourceSpan span}) {
50 | _logFunction(asset, LogLevel.WARNING, message, span);
51 | }
52 |
53 | /// Logs an error message.
54 | ///
55 | /// If [asset] is provided, the log entry is associated with that asset.
56 | /// Otherwise it's associated with the primary input of [transformer]. If
57 | /// present, [span] indicates the location in the input asset that caused the
58 | /// error.
59 | ///
60 | /// Logging any errors will cause Barback to consider the transformation to
61 | /// have failed, much like throwing an exception. This means that neither the
62 | /// primary input nor any outputs emitted by the transformer will be passed on
63 | /// to the following phase, and the build will be reported as having failed.
64 | ///
65 | /// Unlike throwing an exception, this doesn't cause a transformer to stop
66 | /// running. This makes it useful in cases where a single input may have
67 | /// multiple errors that the user wants to know about.
68 | void error(String message, {AssetId asset, SourceSpan span}) {
69 | _logFunction(asset, LogLevel.ERROR, message, span);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/src/transformer/wrapping_aggregate_transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.wrapping_aggregate_transformer;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_id.dart';
10 | import 'aggregate_transform.dart';
11 | import 'aggregate_transformer.dart';
12 | import 'declaring_aggregate_transform.dart';
13 | import 'declaring_aggregate_transformer.dart';
14 | import 'declaring_transform.dart';
15 | import 'declaring_transformer.dart';
16 | import 'lazy_aggregate_transformer.dart';
17 | import 'lazy_transformer.dart';
18 | import 'transform.dart';
19 | import 'transformer.dart';
20 |
21 | /// An [AggregateTransformer] that wraps a non-aggregate [Transformer].
22 | ///
23 | /// Although barback internally works in terms of [AggregateTransformer]s, most
24 | /// transformers only work on individual primary inputs in isolation. We want to
25 | /// allow those transformers to implement the more user-friendly [Transformer]
26 | /// interface. This class makes that possible.
27 | class WrappingAggregateTransformer implements AggregateTransformer {
28 | /// The wrapped transformer.
29 | final Transformer transformer;
30 |
31 | factory WrappingAggregateTransformer(Transformer transformer) {
32 | if (transformer is LazyTransformer) {
33 | return new _LazyWrappingAggregateTransformer(
34 | transformer as LazyTransformer);
35 | } else if (transformer is DeclaringTransformer) {
36 | return new _DeclaringWrappingAggregateTransformer(
37 | transformer as DeclaringTransformer);
38 | } else {
39 | return new WrappingAggregateTransformer._(transformer);
40 | }
41 | }
42 |
43 | WrappingAggregateTransformer._(this.transformer);
44 |
45 | Future classifyPrimary(AssetId id) {
46 | return new Future.sync(() => transformer.isPrimary(id))
47 | .then((isPrimary) => isPrimary ? id.path : null);
48 | }
49 |
50 | Future apply(AggregateTransform aggregateTransform) {
51 | return newTransform(aggregateTransform)
52 | .then((transform) => transformer.apply(transform));
53 | }
54 |
55 | String toString() => transformer.toString();
56 | }
57 |
58 | /// A wrapper for [DeclaringTransformer]s that implements
59 | /// [DeclaringAggregateTransformer].
60 | class _DeclaringWrappingAggregateTransformer
61 | extends WrappingAggregateTransformer
62 | implements DeclaringAggregateTransformer {
63 | _DeclaringWrappingAggregateTransformer(DeclaringTransformer transformer)
64 | : super._(transformer as Transformer);
65 |
66 | Future declareOutputs(DeclaringAggregateTransform aggregateTransform) {
67 | return newDeclaringTransform(aggregateTransform).then((transform) {
68 | return (transformer as DeclaringTransformer).declareOutputs(transform);
69 | });
70 | }
71 | }
72 |
73 | /// A wrapper for [LazyTransformer]s that implements
74 | /// [LazyAggregateTransformer].
75 | class _LazyWrappingAggregateTransformer
76 | extends _DeclaringWrappingAggregateTransformer
77 | implements LazyAggregateTransformer {
78 | _LazyWrappingAggregateTransformer(LazyTransformer transformer)
79 | : super(transformer);
80 | }
81 |
--------------------------------------------------------------------------------
/lib/src/graph/static_asset_cascade.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.graph.static_asset_cascade;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:async/async.dart';
10 | import 'package:collection/collection.dart';
11 |
12 | import '../asset/asset_id.dart';
13 | import '../asset/asset_node.dart';
14 | import '../asset/asset_set.dart';
15 | import '../errors.dart';
16 | import '../log.dart';
17 | import '../package_provider.dart';
18 | import 'asset_cascade.dart';
19 | import 'node_status.dart';
20 | import 'package_graph.dart';
21 |
22 | /// An asset cascade for a static package.
23 | ///
24 | /// A static package is known to have no transformers and no changes to its
25 | /// assets. This allows this class to lazily and efficiently provide assets to
26 | /// the rest of the package graph.
27 | class StaticAssetCascade implements AssetCascade {
28 | final String package;
29 |
30 | final PackageGraph graph;
31 |
32 | /// All sources that have been requested from the provider.
33 | final _sources = new Map>();
34 |
35 | StaticAssetCascade(this.graph, this.package);
36 |
37 | Stream get errors => _errorsController.stream;
38 | final _errorsController =
39 | new StreamController.broadcast(sync: true);
40 |
41 | final status = NodeStatus.IDLE;
42 |
43 | final Stream onLog = new StreamController.broadcast().stream;
44 | final Stream onStatusChange =
45 | new StreamController.broadcast().stream;
46 | final Stream onAsset = new StreamController.broadcast().stream;
47 |
48 | Future get availableOutputs {
49 | var provider = graph.provider as StaticPackageProvider;
50 | return provider
51 | .getAllAssetIds(package)
52 | .asyncMap(provider.getAsset)
53 | .toList()
54 | .then((assets) => new AssetSet.from(DelegatingList.typed(assets)));
55 | }
56 |
57 | Future getAssetNode(AssetId id) {
58 | return _sources.putIfAbsent(id, () {
59 | return DelegatingFuture.typed(graph.provider.getAsset(id).then((asset) {
60 | return new AssetNodeController.available(asset).node;
61 | }).catchError((error, stackTrace) {
62 | if (error is! AssetNotFoundException) {
63 | reportError(new AssetLoadException(id, error, stackTrace));
64 | }
65 |
66 | // TODO(nweiz): propagate error information through asset nodes.
67 | return null;
68 | }));
69 | });
70 | }
71 |
72 | void updateSources(Iterable sources) =>
73 | throw new UnsupportedError("Static package $package can't be explicitly "
74 | "provided sources.");
75 |
76 | void removeSources(Iterable sources) =>
77 | throw new UnsupportedError("Static package $package can't be explicitly "
78 | "provided sources.");
79 |
80 | void updateTransformers(Iterable transformersIterable) =>
81 | throw new UnsupportedError("Static package $package can't have "
82 | "transformers.");
83 |
84 | void forceAllTransforms() {}
85 |
86 | void reportError(BarbackException error) => _errorsController.add(error);
87 |
88 | String toString() => "static cascade for $package";
89 | }
90 |
--------------------------------------------------------------------------------
/test/stream_replayer_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.stream_replayer_test;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/src/utils.dart';
10 | import 'package:barback/src/utils/stream_replayer.dart';
11 | import 'package:unittest/unittest.dart';
12 |
13 | import 'utils.dart';
14 |
15 | main() {
16 | initConfig();
17 |
18 | test(
19 | "a replay that's retrieved before the stream is finished replays the "
20 | "stream", () {
21 | var controller = new StreamController();
22 | var replay = new StreamReplayer(controller.stream).getReplay();
23 |
24 | controller.add(1);
25 | controller.add(2);
26 | controller.add(3);
27 | controller.close();
28 |
29 | expect(replay.toList(), completion(equals([1, 2, 3])));
30 | });
31 |
32 | test(
33 | "a replay that's retrieved after the stream is finished replays the "
34 | "stream", () {
35 | var controller = new StreamController();
36 | var replayer = new StreamReplayer(controller.stream);
37 |
38 | controller.add(1);
39 | controller.add(2);
40 | controller.add(3);
41 | controller.close();
42 |
43 | expect(replayer.getReplay().toList(), completion(equals([1, 2, 3])));
44 | });
45 |
46 | test("multiple replays each replay the stream", () {
47 | var controller = new StreamController();
48 | var replayer = new StreamReplayer(controller.stream);
49 |
50 | var replay1 = replayer.getReplay();
51 | controller.add(1);
52 | controller.add(2);
53 | controller.add(3);
54 | controller.close();
55 | var replay2 = replayer.getReplay();
56 |
57 | expect(replay1.toList(), completion(equals([1, 2, 3])));
58 | expect(replay2.toList(), completion(equals([1, 2, 3])));
59 | });
60 |
61 | test("the replayed stream doesn't close until the source stream closes", () {
62 | var controller = new StreamController();
63 | var replay = new StreamReplayer(controller.stream).getReplay();
64 | var isClosed = false;
65 | replay.last.then((_) {
66 | isClosed = true;
67 | });
68 |
69 | controller.add(1);
70 | controller.add(2);
71 | controller.add(3);
72 |
73 | expect(
74 | pumpEventQueue().then((_) {
75 | expect(isClosed, isFalse);
76 | controller.close();
77 | return pumpEventQueue();
78 | }).then((_) {
79 | expect(isClosed, isTrue);
80 | }),
81 | completes);
82 | });
83 |
84 | test("the wrapped stream isn't opened if there are no replays", () {
85 | var isOpened = false;
86 | var controller = new StreamController(onListen: () {
87 | isOpened = true;
88 | });
89 | new StreamReplayer(controller.stream);
90 |
91 | expect(pumpEventQueue().then((_) => isOpened), completion(isFalse));
92 | });
93 |
94 | test("the wrapped stream isn't opened if no replays are opened", () {
95 | var isOpened = false;
96 | var controller = new StreamController(onListen: () {
97 | isOpened = true;
98 | });
99 | var replayer = new StreamReplayer(controller.stream);
100 | replayer.getReplay();
101 | replayer.getReplay();
102 |
103 | expect(pumpEventQueue().then((_) => isOpened), completion(isFalse));
104 | });
105 | }
106 |
--------------------------------------------------------------------------------
/test/package_graph/get_all_assets_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.barback_test;
6 |
7 | import 'dart:async';
8 |
9 | import 'package:barback/barback.dart';
10 | import 'package:scheduled_test/scheduled_test.dart';
11 |
12 | import '../utils.dart';
13 |
14 | main() {
15 | initConfig();
16 |
17 | test("gets all source assets", () {
18 | initGraph(["app|a.txt", "app|b.txt", "app|c.txt"]);
19 | updateSources(["app|a.txt", "app|b.txt", "app|c.txt"]);
20 | expectAllAssets(["app|a.txt", "app|b.txt", "app|c.txt"]);
21 | buildShouldSucceed();
22 | });
23 |
24 | test("includes transformed outputs", () {
25 | initGraph([
26 | "app|a.txt",
27 | "app|foo.blub"
28 | ], {
29 | "app": [
30 | [new RewriteTransformer("blub", "blab")]
31 | ]
32 | });
33 | updateSources(["app|a.txt", "app|foo.blub"]);
34 | expectAllAssets(["app|a.txt", "app|foo.blub", "app|foo.blab"]);
35 | buildShouldSucceed();
36 | });
37 |
38 | test("includes overwritten outputs", () {
39 | initGraph([
40 | "app|a.txt",
41 | "app|foo.blub"
42 | ], {
43 | "app": [
44 | [new RewriteTransformer("blub", "blub")]
45 | ]
46 | });
47 | updateSources(["app|a.txt", "app|foo.blub"]);
48 | expectAllAssets({"app|a.txt": "a", "app|foo.blub": "foo.blub"});
49 | buildShouldSucceed();
50 | });
51 |
52 | test("completes to an error if two transformers output the same file", () {
53 | initGraph([
54 | "app|foo.a"
55 | ], {
56 | "app": [
57 | [new RewriteTransformer("a", "b"), new RewriteTransformer("a", "b")]
58 | ]
59 | });
60 | updateSources(["app|foo.a"]);
61 | expectAllAssetsShouldFail(isAssetCollisionException("app|foo.b"));
62 | });
63 |
64 | test("completes to an error if a transformer fails", () {
65 | initGraph([
66 | "app|foo.txt"
67 | ], {
68 | "app": [
69 | [
70 | new BadTransformer(["app|foo.out"])
71 | ]
72 | ]
73 | });
74 |
75 | updateSources(["app|foo.txt"]);
76 | expectAllAssetsShouldFail(
77 | isTransformerException(equals(BadTransformer.ERROR)));
78 | });
79 |
80 | test("completes to an aggregate error if there are multiple errors", () {
81 | initGraph([
82 | "app|foo.txt"
83 | ], {
84 | "app": [
85 | [
86 | new BadTransformer(["app|foo.out"]),
87 | new BadTransformer(["app|foo.out2"])
88 | ]
89 | ]
90 | });
91 |
92 | updateSources(["app|foo.txt"]);
93 | expectAllAssetsShouldFail(isAggregateException([
94 | isTransformerException(equals(BadTransformer.ERROR)),
95 | isTransformerException(equals(BadTransformer.ERROR))
96 | ]));
97 | });
98 |
99 | // Regression test.
100 | test(
101 | "getAllAssets() is called synchronously after after initializing "
102 | "barback", () {
103 | var provider = new MockProvider(
104 | {"app|a.txt": "a", "app|b.txt": "b", "app|c.txt": "c"});
105 | var barback = new Barback(provider);
106 | barback.updateSources([
107 | new AssetId.parse("app|a.txt"),
108 | new AssetId.parse("app|b.txt"),
109 | new AssetId.parse("app|c.txt")
110 | ]);
111 |
112 | expect(
113 | barback.getAllAssets().then((assets) {
114 | return Future.wait(assets.map((asset) => asset.readAsString()));
115 | }),
116 | completion(unorderedEquals(["a", "b", "c"])));
117 | });
118 | }
119 |
--------------------------------------------------------------------------------
/lib/src/transformer/transformer.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.transformer;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_id.dart';
10 | import '../utils.dart';
11 | import 'transform.dart';
12 |
13 | /// A [Transformer] represents a processor that takes in one or more input
14 | /// assets and uses them to generate one or more output assets.
15 | ///
16 | /// Dart2js, a SASS->CSS processor, a CSS spriter, and a tool to concatenate
17 | /// files are all examples of transformers. To define your own transformation
18 | /// step, extend (or implement) this class.
19 | ///
20 | /// If possible, transformers should implement [DeclaringTransformer] as well to
21 | /// help barback optimize the package graph.
22 | abstract class Transformer {
23 | /// Override this to return a space-separated list of file extensions that are
24 | /// allowed for the primary inputs to this transformer.
25 | ///
26 | /// Each extension must begin with a leading `.`.
27 | ///
28 | /// If you don't override [isPrimary] yourself, it defaults to allowing any
29 | /// asset whose extension matches one of the ones returned by this. If you
30 | /// don't override [isPrimary] *or* this, it allows all files.
31 | String get allowedExtensions => null;
32 |
33 | Transformer() {
34 | if (allowedExtensions == null) return;
35 |
36 | var invalidExtensions = allowedExtensions
37 | .split(" ")
38 | .where((extension) => !extension.startsWith("."))
39 | .map((extension) => '"$extension"');
40 | if (invalidExtensions.isEmpty) return;
41 |
42 | throw new FormatException('Each extension in $this.allowedExtensions '
43 | 'must begin with a ".", but ${toSentence(invalidExtensions)} '
44 | '${pluralize("doesn't", invalidExtensions.length, plural: "don't")}.');
45 | }
46 |
47 | /// Returns `true` if [id] can be a primary input for this transformer.
48 | ///
49 | /// While a transformer can read from multiple input files, one must be the
50 | /// "primary" input. This asset determines whether the transformation should
51 | /// be run at all. If the primary input is removed, the transformer will no
52 | /// longer be run.
53 | ///
54 | /// A concrete example is dart2js. When you run dart2js, it will traverse
55 | /// all of the imports in your Dart source files and use the contents of all
56 | /// of those to generate the final JS. However you still run dart2js "on" a
57 | /// single file: the entrypoint Dart file that has your `main()` method.
58 | /// This entrypoint file would be the primary input.
59 | ///
60 | /// If this is not overridden, defaults to allow any asset whose extension
61 | /// matches one of the ones returned by [allowedExtensions]. If *that* is
62 | /// not overridden, allows all assets.
63 | ///
64 | /// This may return a `Future` or, if it's entirely synchronous, a
65 | /// `bool`.
66 | isPrimary(AssetId id) {
67 | // Allow all files if [primaryExtensions] is not overridden.
68 | if (allowedExtensions == null) return true;
69 |
70 | for (var extension in allowedExtensions.split(" ")) {
71 | if (id.path.endsWith(extension)) return true;
72 | }
73 |
74 | return false;
75 | }
76 |
77 | /// Run this transformer on the primary input specified by [transform].
78 | ///
79 | /// The [transform] is used by the [Transformer] for two purposes (in
80 | /// addition to accessing the primary input). It can call `getInput()` to
81 | /// request additional input assets. It also calls `addOutput()` to provide
82 | /// generated assets back to the system. Either can be called multiple times,
83 | /// in any order.
84 | ///
85 | /// In other words, a Transformer's job is to find all inputs for a
86 | /// transform, starting at the primary input, then generate all output assets
87 | /// and yield them back to the transform.
88 | ///
89 | /// If this does asynchronous work, it should return a [Future] that completes
90 | /// once it's finished.
91 | apply(Transform transform);
92 |
93 | String toString() => runtimeType.toString().replaceAll("Transformer", "");
94 | }
95 |
--------------------------------------------------------------------------------
/lib/src/transformer/declaring_aggregate_transform.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.declaring_aggregate_transform;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_id.dart';
10 | import '../graph/transform_node.dart';
11 | import '../utils.dart';
12 | import 'base_transform.dart';
13 |
14 | /// A transform for [DeclaringAggregateTransformer]s that allows them to declare
15 | /// the ids of the outputs they'll generate without generating the concrete
16 | /// bodies of those outputs.
17 | class DeclaringAggregateTransform extends BaseTransform {
18 | /// The set of output ids declared by the transformer.
19 | final _outputIds = new Set();
20 |
21 | /// The transform key.
22 | ///
23 | /// This is the key returned by [AggregateTransformer.classifyPrimary] for all
24 | /// the assets in this transform.
25 | final String key;
26 |
27 | /// The package in which this transform is running.
28 | final String package;
29 |
30 | /// The stream of primary input ids that have been aggregated for this
31 | /// transform.
32 | ///
33 | /// This is exposed as a stream so that the transformer can start working
34 | /// before all its input ids are available. The stream is closed not just when
35 | /// all inputs are provided, but when barback is confident no more inputs will
36 | /// be forthcoming.
37 | ///
38 | /// A transformer may complete its `declareOutputs` method before this stream
39 | /// is closed. For example, it may know that each key will only have two
40 | /// inputs associated with it, and so use `transform.primaryIds.take(2)` to
41 | /// access only those inputs' ids.
42 | Stream get primaryIds => _primaryIds;
43 | Stream _primaryIds;
44 |
45 | /// The controller for [primaryIds].
46 | ///
47 | /// This is a broadcast controller so that the transform can keep
48 | /// [_emittedPrimaryIds] up to date.
49 | final _idController = new StreamController.broadcast();
50 |
51 | /// The set of all primary input ids that have been emitted by [primaryIds].
52 | final _emittedPrimaryIds = new Set();
53 |
54 | DeclaringAggregateTransform._(TransformNode node)
55 | : key = node.key,
56 | package = node.phase.cascade.package,
57 | super(node) {
58 | _idController.stream.listen(_emittedPrimaryIds.add);
59 | // [primaryIds] should be a non-broadcast stream.
60 | _primaryIds = broadcastToSingleSubscription(_idController.stream);
61 | }
62 |
63 | /// Stores [id] as the id of an output that will be created by this
64 | /// transformation when it's run.
65 | ///
66 | /// A transformation can declare as many assets as it wants. If
67 | /// [DeclaringTransformer.declareOutputs] declares a given asset id for a
68 | /// given input, [Transformer.apply] should emit the corresponding asset as
69 | /// well.
70 | void declareOutput(AssetId id) {
71 | // TODO(nweiz): This should immediately throw if an output with that ID
72 | // has already been declared by this transformer.
73 | _outputIds.add(id);
74 | }
75 |
76 | void consumePrimary(AssetId id) {
77 | if (!_emittedPrimaryIds.contains(id)) {
78 | throw new StateError(
79 | "$id can't be consumed because it's not a primary input.");
80 | }
81 |
82 | super.consumePrimary(id);
83 | }
84 | }
85 |
86 | /// The controller for [DeclaringAggregateTransform].
87 | class DeclaringAggregateTransformController extends BaseTransformController {
88 | final DeclaringAggregateTransform transform;
89 |
90 | /// The set of ids that the transformer declares it will emit.
91 | Set get outputIds => transform._outputIds;
92 |
93 | bool get isDone => transform._idController.isClosed;
94 |
95 | DeclaringAggregateTransformController(TransformNode node)
96 | : transform = new DeclaringAggregateTransform._(node);
97 |
98 | /// Adds a primary input id to the [DeclaringAggregateTransform.primaryIds]
99 | /// stream.
100 | void addId(AssetId id) => transform._idController.add(id);
101 |
102 | void done() {
103 | transform._idController.close();
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/lib/src/asset/asset_id.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.asset.asset_id;
6 |
7 | import 'package:path/path.dart' as pathos;
8 |
9 | /// Identifies an asset within a package.
10 | class AssetId implements Comparable {
11 | /// The name of the package containing this asset.
12 | final String package;
13 |
14 | /// The path to the asset relative to the root directory of [package].
15 | ///
16 | /// Source (i.e. read from disk) and generated (i.e. the output of a
17 | /// [Transformer]) assets all have paths. Even intermediate assets that are
18 | /// generated and then consumed by later transformations will still have
19 | /// a path used to identify it.
20 | ///
21 | /// Asset paths always use forward slashes as path separators, regardless of
22 | /// the host platform.
23 | final String path;
24 |
25 | /// Gets the file extension of the asset, if it has one, including the ".".
26 | String get extension => pathos.extension(path);
27 |
28 | /// Creates a new AssetId at [path] within [package].
29 | ///
30 | /// The [path] will be normalized: any backslashes will be replaced with
31 | /// forward slashes (regardless of host OS) and "." and ".." will be removed
32 | /// where possible.
33 | AssetId(this.package, String path) : path = _normalizePath(path);
34 |
35 | /// Parses an [AssetId] string of the form "package|path/to/asset.txt".
36 | ///
37 | /// The [path] will be normalized: any backslashes will be replaced with
38 | /// forward slashes (regardless of host OS) and "." and ".." will be removed
39 | /// where possible.
40 | factory AssetId.parse(String description) {
41 | var parts = description.split("|");
42 | if (parts.length != 2) {
43 | throw new FormatException('Could not parse "$description".');
44 | }
45 |
46 | if (parts[0].isEmpty) {
47 | throw new FormatException(
48 | 'Cannot have empty package name in "$description".');
49 | }
50 |
51 | if (parts[1].isEmpty) {
52 | throw new FormatException('Cannot have empty path in "$description".');
53 | }
54 |
55 | return new AssetId(parts[0], parts[1]);
56 | }
57 |
58 | /// Deserializes an [AssetId] from [data], which must be the result of
59 | /// calling [serialize] on an existing [AssetId].
60 | ///
61 | /// Note that this is intended for communicating ids across isolates and not
62 | /// for persistent storage of asset identifiers. There is no guarantee of
63 | /// backwards compatibility in serialization form across versions.
64 | AssetId.deserialize(data)
65 | : package = data[0],
66 | path = data[1];
67 |
68 | /// Returns `true` of [other] is an [AssetId] with the same package and path.
69 | operator ==(other) =>
70 | other is AssetId && package == other.package && path == other.path;
71 |
72 | int get hashCode => package.hashCode ^ path.hashCode;
73 |
74 | int compareTo(AssetId other) {
75 | var packageComp = package.compareTo(other.package);
76 | if (packageComp != 0) return packageComp;
77 | return path.compareTo(other.path);
78 | }
79 |
80 | /// Returns a new [AssetId] with the same [package] as this one and with the
81 | /// [path] extended to include [extension].
82 | AssetId addExtension(String extension) =>
83 | new AssetId(package, "$path$extension");
84 |
85 | /// Returns a new [AssetId] with the same [package] and [path] as this one
86 | /// but with file extension [newExtension].
87 | AssetId changeExtension(String newExtension) =>
88 | new AssetId(package, pathos.withoutExtension(path) + newExtension);
89 |
90 | String toString() => "$package|$path";
91 |
92 | /// Serializes this [AssetId] to an object that can be sent across isolates
93 | /// and passed to [deserialize].
94 | serialize() => [package, path];
95 | }
96 |
97 | String _normalizePath(String path) {
98 | if (pathos.isAbsolute(path)) {
99 | throw new ArgumentError('Asset paths must be relative, but got "$path".');
100 | }
101 |
102 | // Normalize path separators so that they are always "/" in the AssetID.
103 | path = path.replaceAll(r"\", "/");
104 |
105 | // Collapse "." and "..".
106 | return pathos.posix.normalize(path);
107 | }
108 |
--------------------------------------------------------------------------------
/lib/src/serialize.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.serialize;
6 |
7 | import 'dart:async';
8 | import 'dart:isolate';
9 |
10 | import 'package:stack_trace/stack_trace.dart';
11 |
12 | import 'asset/asset_id.dart';
13 | import 'utils.dart';
14 |
15 | /// Converts [id] into a serializable map.
16 | Map serializeId(AssetId id) => {'package': id.package, 'path': id.path};
17 |
18 | /// Converts [stream] into a [SendPort] with which another isolate can request
19 | /// the data from [stream].
20 | SendPort serializeStream(Stream stream) {
21 | var receivePort = new ReceivePort();
22 | receivePort.first.then((sendPort) {
23 | stream.listen((data) => sendPort.send({'type': 'data', 'data': data}),
24 | onDone: () => sendPort.send({'type': 'done'}),
25 | onError: (error, stackTrace) {
26 | sendPort.send({
27 | 'type': 'error',
28 | 'error': CrossIsolateException.serialize(error, stackTrace)
29 | });
30 | });
31 | });
32 |
33 | return receivePort.sendPort;
34 | }
35 |
36 | /// Converts a serializable map into an [AssetId].
37 | AssetId deserializeId(Map id) => new AssetId(id['package'], id['path']);
38 |
39 | /// Convert a [SendPort] whose opposite is waiting to send us a stream into a
40 | /// [Stream].
41 | ///
42 | /// No stream data will actually be sent across the isolate boundary until
43 | /// someone subscribes to the returned stream.
44 | Stream deserializeStream(SendPort sendPort) {
45 | return callbackStream(() {
46 | var receivePort = new ReceivePort();
47 | sendPort.send(receivePort.sendPort);
48 | return receivePort
49 | .transform(const StreamTransformer(_deserializeTransformer));
50 | });
51 | }
52 |
53 | /// The body of a [StreamTransformer] that deserializes the values in a stream
54 | /// sent by [serializeStream].
55 | StreamSubscription _deserializeTransformer(Stream input, bool cancelOnError) {
56 | var subscription;
57 | var transformed = input
58 | .transform(new StreamTransformer.fromHandlers(handleData: (data, sink) {
59 | if (data['type'] == 'data') {
60 | sink.add(data['data']);
61 | } else if (data['type'] == 'error') {
62 | var exception = new CrossIsolateException.deserialize(data['error']);
63 | sink.addError(exception, exception.stackTrace);
64 | } else {
65 | assert(data['type'] == 'done');
66 | sink.close();
67 | subscription.cancel();
68 | }
69 | }));
70 | subscription = transformed.listen(null, cancelOnError: cancelOnError);
71 | return subscription;
72 | }
73 |
74 | /// An exception that was originally raised in another isolate.
75 | ///
76 | /// Exception objects can't cross isolate boundaries in general, so this class
77 | /// wraps as much information as can be consistently serialized.
78 | class CrossIsolateException implements Exception {
79 | /// The name of the type of exception thrown.
80 | ///
81 | /// This is the return value of [error.runtimeType.toString()]. Keep in mind
82 | /// that objects in different libraries may have the same type name.
83 | final String type;
84 |
85 | /// The exception's message, or its [toString] if it didn't expose a `message`
86 | /// property.
87 | final String message;
88 |
89 | /// The exception's stack chain, or `null` if no stack chain was available.
90 | final Chain stackTrace;
91 |
92 | /// Loads a [CrossIsolateException] from a serialized representation.
93 | ///
94 | /// [error] should be the result of [CrossIsolateException.serialize].
95 | CrossIsolateException.deserialize(Map error)
96 | : type = error['type'],
97 | message = error['message'],
98 | stackTrace =
99 | error['stack'] == null ? null : new Chain.parse(error['stack']);
100 |
101 | /// Serializes [error] to an object that can safely be passed across isolate
102 | /// boundaries.
103 | static Map serialize(error, [StackTrace stack]) {
104 | if (stack == null && error is Error) stack = error.stackTrace;
105 | return {
106 | 'type': error.runtimeType.toString(),
107 | 'message': getErrorMessage(error),
108 | 'stack': stack == null ? null : new Chain.forTrace(stack).toString()
109 | };
110 | }
111 |
112 | String toString() => "$message\n$stackTrace";
113 | }
114 |
--------------------------------------------------------------------------------
/lib/src/graph/phase_output.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.graph.phase_output;
6 |
7 | import 'dart:async';
8 | import 'dart:collection';
9 |
10 | import '../asset/asset_forwarder.dart';
11 | import '../asset/asset_node.dart';
12 | import '../errors.dart';
13 | import 'phase.dart';
14 |
15 | /// A class that handles a single output of a phase.
16 | ///
17 | /// Normally there's only a single [AssetNode] for a phase's output, but it's
18 | /// possible that multiple transformers in the same phase emit assets with the
19 | /// same id, causing collisions. This handles those collisions by forwarding the
20 | /// chronologically first asset.
21 | ///
22 | /// When the asset being forwarding changes, the old value of [output] will be
23 | /// marked as removed and a new value will replace it. Users of this class can
24 | /// be notified of this using [onAsset].
25 | class PhaseOutput {
26 | /// The phase for which this is an output.
27 | final Phase _phase;
28 |
29 | /// A string describing the location of [this] in the transformer graph.
30 | final String _location;
31 |
32 | /// The asset node for this output.
33 | AssetNode get output => _outputForwarder.node;
34 | AssetForwarder _outputForwarder;
35 |
36 | /// A stream that emits an [AssetNode] each time this output starts forwarding
37 | /// a new asset.
38 | Stream get onAsset => _onAssetController.stream;
39 | final _onAssetController =
40 | new StreamController.broadcast(sync: true);
41 |
42 | /// The assets for this output.
43 | ///
44 | /// If there's no collision, this will only have one element. Otherwise, it
45 | /// will be ordered by which asset was added first.
46 | final _assets = new Queue();
47 |
48 | /// The [AssetCollisionException] for this output, or null if there is no
49 | /// collision currently.
50 | AssetCollisionException get collisionException {
51 | if (_assets.length == 1) return null;
52 | return new AssetCollisionException(
53 | _assets
54 | .where((asset) => asset.transform != null)
55 | .map((asset) => asset.transform.info),
56 | output.id);
57 | }
58 |
59 | PhaseOutput(this._phase, AssetNode output, this._location)
60 | : _outputForwarder = new AssetForwarder(output) {
61 | assert(!output.state.isRemoved);
62 | add(output);
63 | }
64 |
65 | /// Adds an asset node as an output with this id.
66 | void add(AssetNode node) {
67 | assert(node.id == output.id);
68 | assert(!output.state.isRemoved);
69 | _assets.add(node);
70 | _watchAsset(node);
71 | }
72 |
73 | /// Removes all existing listeners on [output] without actually closing
74 | /// [this].
75 | ///
76 | /// This marks [output] as removed, but immediately replaces it with a new
77 | /// [AssetNode] in the same state as the old output. This is used when adding
78 | /// a new [Phase] to cause consumers of the prior phase's outputs to be to
79 | /// start consuming the new phase's outputs instead.
80 | void removeListeners() {
81 | _outputForwarder.close();
82 | _outputForwarder = new AssetForwarder(_assets.first);
83 | _onAssetController.add(output);
84 | }
85 |
86 | /// Watches [node] to adjust [_assets] and [output] when it's removed.
87 | void _watchAsset(AssetNode node) {
88 | node.whenRemoved(() {
89 | if (_assets.length == 1) {
90 | assert(_assets.single == node);
91 | _outputForwarder.close();
92 | _onAssetController.close();
93 | return;
94 | }
95 |
96 | // If there was more than one asset, we're resolving a collision --
97 | // possibly partially.
98 | var wasFirst = _assets.first == node;
99 | _assets.remove(node);
100 |
101 | // If this was the first asset, we replace it with the next asset
102 | // (chronologically).
103 | if (wasFirst) removeListeners();
104 |
105 | // If there's still a collision, report it. This lets the user know if
106 | // they've successfully resolved the collision or not.
107 | if (_assets.length > 1) {
108 | // TODO(nweiz): report this through the output asset.
109 | _phase.cascade.reportError(collisionException);
110 | }
111 | });
112 | }
113 |
114 | String toString() => "phase output in $_location for $output";
115 | }
116 |
--------------------------------------------------------------------------------
/lib/src/transformer/base_transform.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.base_transform;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_id.dart';
10 | import '../graph/transform_node.dart';
11 | import '../log.dart';
12 | import 'transform_logger.dart';
13 |
14 | /// The base class for the ephemeral transform objects that are passed to
15 | /// transformers.
16 | ///
17 | /// This class provides the transformers with inputs, but its up to the
18 | /// subclasses to provide a means of emitting outputs.
19 | abstract class BaseTransform {
20 | final TransformNode _node;
21 |
22 | /// The ids of primary inputs that should be consumed.
23 | ///
24 | /// This is exposed by [BaseTransformController].
25 | final _consumedPrimaries = new Set();
26 |
27 | /// Whether the transformer logged an error.
28 | ///
29 | /// This is exposed via [BaseTransformController].
30 | bool _loggedError = false;
31 |
32 | /// The controller for the stream of log entries emitted by the transformer.
33 | ///
34 | /// This is exposed via [BaseTransformController].
35 | ///
36 | /// This is synchronous because error logs can cause the transform to fail, so
37 | /// we need to ensure that their processing isn't delayed until after the
38 | /// transform or build has finished.
39 | final _onLogController = new StreamController.broadcast(sync: true);
40 |
41 | /// A logger so that the [Transformer] can report build details.
42 | TransformLogger get logger => _logger;
43 | TransformLogger _logger;
44 |
45 | BaseTransform(this._node) {
46 | _logger = new TransformLogger((asset, level, message, span) {
47 | if (level == LogLevel.ERROR) _loggedError = true;
48 |
49 | // If the log isn't already associated with an asset, use the primary.
50 | if (asset == null) asset = _node.info.primaryId;
51 | var entry = new LogEntry(_node.info, asset, level, message, span);
52 |
53 | // The log controller can be closed while log entries are still coming in
54 | // if the transformer is removed during [apply].
55 | if (!_onLogController.isClosed) _onLogController.add(entry);
56 | });
57 | }
58 |
59 | /// Consume a primary input so that it doesn't get processed by future
60 | /// phases or emitted once processing has finished.
61 | ///
62 | /// Normally each primary input will automatically be forwarded unless the
63 | /// transformer overwrites it by emitting an input with the same id. This
64 | /// allows the transformer to tell barback not to forward a primary input
65 | /// even if it's not overwritten.
66 | void consumePrimary(AssetId id) {
67 | // TODO(nweiz): throw an error if an id is consumed that wasn't listed as a
68 | // primary input.
69 | _consumedPrimaries.add(id);
70 | }
71 | }
72 |
73 | /// The base class for controllers of subclasses of [BaseTransform].
74 | ///
75 | /// Controllers are used so that [TransformNode]s can get values from a
76 | /// [BaseTransform] without exposing getters in the public API.
77 | abstract class BaseTransformController {
78 | /// The [BaseTransform] controlled by this controller.
79 | BaseTransform get transform;
80 |
81 | /// The ids of primary inputs that should be consumed.
82 | Set get consumedPrimaries => transform._consumedPrimaries;
83 |
84 | /// Whether the transform logged an error.
85 | bool get loggedError => transform._loggedError;
86 |
87 | /// The stream of log entries emitted by the transformer during a run.
88 | Stream get onLog => transform._onLogController.stream;
89 |
90 | /// Whether the transform's input or id stream has been closed.
91 | ///
92 | /// See also [done].
93 | bool get isDone;
94 |
95 | /// Mark this transform as finished emitting new inputs or input ids.
96 | ///
97 | /// This is distinct from [cancel] in that it *doesn't* indicate that the
98 | /// transform is finished being used entirely. The transformer may still log
99 | /// messages and load secondary inputs. This just indicates that all the
100 | /// primary inputs are accounted for.
101 | void done();
102 |
103 | /// Mark this transform as canceled.
104 | ///
105 | /// This will close any streams and release any resources that were allocated
106 | /// for the duration of the transformation. Unlike [done], this indicates that
107 | /// the transformation is no longer relevant; either it has returned, or
108 | /// something external has preemptively invalidated its results.
109 | void cancel() {
110 | done();
111 | transform._onLogController.close();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/lib/src/transformer/transform.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.transformer.transform;
6 |
7 | import 'dart:async';
8 | import 'dart:convert';
9 |
10 | import '../asset/asset.dart';
11 | import '../asset/asset_id.dart';
12 | import '../errors.dart';
13 | import 'aggregate_transform.dart';
14 | import 'transform_logger.dart';
15 |
16 | /// Creates a new [Transform] wrapping an [AggregateTransform].
17 | ///
18 | /// Although barback internally works in terms of [AggregateTransformer]s, most
19 | /// transformers only work on individual primary inputs in isolation. We want to
20 | /// allow those transformers to implement the more user-friendly [Transformer]
21 | /// interface which takes the more user-friendly [Transform] object. This method
22 | /// wraps the more general [AggregateTransform] to return a [Transform] instead.
23 | Future newTransform(AggregateTransform aggregate) {
24 | // A wrapped [Transformer] will assign each primary input a unique transform
25 | // key, so we can safely get the first asset emitted. We don't want to wait
26 | // for the stream to close, since that requires barback to prove that no more
27 | // new assets will be generated.
28 | return aggregate.primaryInputs.first
29 | .then((primaryInput) => new Transform._(aggregate, primaryInput));
30 | }
31 |
32 | /// While a [Transformer] represents a *kind* of transformation, this defines
33 | /// one specific usage of it on a set of files.
34 | ///
35 | /// This ephemeral object exists only during an actual transform application to
36 | /// facilitate communication between the [Transformer] and the code hosting
37 | /// the transformation. It lets the [Transformer] access inputs and generate
38 | /// outputs.
39 | class Transform {
40 | /// The underlying aggregate transform.
41 | final AggregateTransform _aggregate;
42 |
43 | /// Gets the primary input asset.
44 | ///
45 | /// While a transformation can use multiple input assets, one must be a
46 | /// special "primary" asset. This will be the "entrypoint" or "main" input
47 | /// file for a transformation.
48 | ///
49 | /// For example, with a dart2js transform, the primary input would be the
50 | /// entrypoint Dart file. All of the other Dart files that that imports
51 | /// would be secondary inputs.
52 | final Asset primaryInput;
53 |
54 | /// A logger so that the [Transformer] can report build details.
55 | TransformLogger get logger => _aggregate.logger;
56 |
57 | Transform._(this._aggregate, this.primaryInput);
58 |
59 | /// Gets the asset for an input [id].
60 | ///
61 | /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
62 | Future getInput(AssetId id) => _aggregate.getInput(id);
63 |
64 | /// A convenience method to the contents of the input with [id] as a string.
65 | ///
66 | /// This is equivalent to calling [getInput] followed by [Asset.readAsString].
67 | ///
68 | /// If the asset was created from a [String] the original string is always
69 | /// returned and [encoding] is ignored. Otherwise, the binary data of the
70 | /// asset is decoded using [encoding], which defaults to [utf8].
71 | ///
72 | /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
73 | Future readInputAsString(AssetId id, {Encoding encoding}) =>
74 | _aggregate.readInputAsString(id, encoding: encoding);
75 |
76 | /// A convenience method to the contents of the input with [id].
77 | ///
78 | /// This is equivalent to calling [getInput] followed by [Asset.read].
79 | ///
80 | /// If the asset was created from a [String], this returns its UTF-8 encoding.
81 | ///
82 | /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
83 | Stream> readInput(AssetId id) => _aggregate.readInput(id);
84 |
85 | /// A convenience method to return whether or not an asset exists.
86 | ///
87 | /// This is equivalent to calling [getInput] and catching an
88 | /// [AssetNotFoundException].
89 | Future hasInput(AssetId id) => _aggregate.hasInput(id);
90 |
91 | /// Stores [output] as an output created by this transformation.
92 | ///
93 | /// A transformation can output as many assets as it wants.
94 | void addOutput(Asset output) => _aggregate.addOutput(output);
95 |
96 | /// Consume the primary input so that it doesn't get processed by future
97 | /// phases or emitted once processing has finished.
98 | ///
99 | /// Normally the primary input will automatically be forwarded unless the
100 | /// transformer overwrites it by emitting an input with the same id. This
101 | /// allows the transformer to tell barback not to forward the primary input
102 | /// even if it's not overwritten.
103 | void consumePrimary() => _aggregate.consumePrimary(primaryInput.id);
104 | }
105 |
--------------------------------------------------------------------------------
/test/package_graph/source_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.package_graph.source_test;
6 |
7 | import 'package:scheduled_test/scheduled_test.dart';
8 |
9 | import '../utils.dart';
10 |
11 | main() {
12 | initConfig();
13 | test("gets a source asset", () {
14 | initGraph(["app|foo.txt"]);
15 | updateSources(["app|foo.txt"]);
16 | expectAsset("app|foo.txt");
17 | buildShouldSucceed();
18 | });
19 |
20 | test("doesn't get an unknown source", () {
21 | initGraph();
22 | expectNoAsset("app|unknown.txt");
23 | });
24 |
25 | test("doesn't get an unprovided source", () {
26 | initGraph();
27 | updateSources(["app|unknown.txt"]);
28 | expectNoAsset("app|unknown.txt");
29 | });
30 |
31 | test("doesn't get an asset that isn't an updated source", () {
32 | initGraph(["app|foo.txt"]);
33 |
34 | // Sources must be explicitly made visible to barback by calling
35 | // updateSources() on them. It isn't enough for the provider to be able
36 | // to provide it.
37 | //
38 | // This lets you distinguish between sources that you want to be primaries
39 | // and the larger set of inputs that those primaries are allowed to pull in.
40 | expectNoAsset("app|foo.txt");
41 | });
42 |
43 | test("gets a source asset if not transformed", () {
44 | initGraph([
45 | "app|foo.txt"
46 | ], {
47 | "app": [
48 | [new RewriteTransformer("nottxt", "whatever")]
49 | ]
50 | });
51 |
52 | updateSources(["app|foo.txt"]);
53 | expectAsset("app|foo.txt");
54 | buildShouldSucceed();
55 | });
56 |
57 | test("doesn't get a removed source", () {
58 | initGraph(["app|foo.txt"]);
59 |
60 | updateSources(["app|foo.txt"]);
61 | expectAsset("app|foo.txt");
62 | buildShouldSucceed();
63 |
64 | removeSources(["app|foo.txt"]);
65 | expectNoAsset("app|foo.txt");
66 | buildShouldSucceed();
67 | });
68 |
69 | test("collapses redundant updates", () {
70 | var transformer = new RewriteTransformer("blub", "blab");
71 | initGraph([
72 | "app|foo.blub"
73 | ], {
74 | "app": [
75 | [transformer]
76 | ]
77 | });
78 |
79 | schedule(() {
80 | // Make a bunch of synchronous update calls.
81 | updateSourcesSync(["app|foo.blub"]);
82 | updateSourcesSync(["app|foo.blub"]);
83 | updateSourcesSync(["app|foo.blub"]);
84 | updateSourcesSync(["app|foo.blub"]);
85 | });
86 |
87 | expectAsset("app|foo.blab", "foo.blab");
88 | buildShouldSucceed();
89 |
90 | expect(transformer.numRuns, completion(equals(1)));
91 | });
92 |
93 | test("a removal cancels out an update", () {
94 | initGraph(["app|foo.txt"]);
95 |
96 | schedule(() {
97 | updateSourcesSync(["app|foo.txt"]);
98 | removeSourcesSync(["app|foo.txt"]);
99 | });
100 |
101 | expectNoAsset("app|foo.txt");
102 | buildShouldSucceed();
103 | });
104 |
105 | test("an update cancels out a removal", () {
106 | initGraph(["app|foo.txt"]);
107 |
108 | schedule(() {
109 | removeSourcesSync(["app|foo.txt"]);
110 | updateSourcesSync(["app|foo.txt"]);
111 | });
112 |
113 | expectAsset("app|foo.txt");
114 | buildShouldSucceed();
115 | });
116 |
117 | test("reloads an asset that's updated while loading", () {
118 | initGraph({"app|foo.txt": "foo"});
119 |
120 | pauseProvider();
121 | // The mock provider synchronously loads the value of the assets, so this
122 | // will kick off two loads with different values. The second one should
123 | // win.
124 | updateSources(["app|foo.txt"]);
125 | modifyAsset("app|foo.txt", "bar");
126 | updateSources(["app|foo.txt"]);
127 |
128 | resumeProvider();
129 | expectAsset("app|foo.txt", "bar");
130 | buildShouldSucceed();
131 | });
132 |
133 | test("restarts a build if a source is updated while sources are loading", () {
134 | var transformer = new RewriteTransformer("txt", "out");
135 | initGraph([
136 | "app|foo.txt",
137 | "app|other.bar"
138 | ], {
139 | "app": [
140 | [transformer]
141 | ]
142 | });
143 |
144 | // Run the whole graph so all nodes are clean.
145 | updateSources(["app|foo.txt", "app|other.bar"]);
146 | expectAsset("app|foo.out", "foo.out");
147 | expectAsset("app|other.bar");
148 |
149 | buildShouldSucceed();
150 |
151 | // Make the provider slow to load a source.
152 | pauseProvider();
153 |
154 | // Update an asset that doesn't trigger any transformers.
155 | updateSources(["app|other.bar"]);
156 |
157 | // Now update an asset that does trigger a transformer.
158 | updateSources(["app|foo.txt"]);
159 |
160 | resumeProvider();
161 |
162 | buildShouldSucceed();
163 |
164 | expect(transformer.numRuns, completion(equals(2)));
165 | });
166 | }
167 |
--------------------------------------------------------------------------------
/lib/src/barback.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.barback;
6 |
7 | import 'dart:async';
8 |
9 | import 'asset/asset.dart';
10 | import 'asset/asset_id.dart';
11 | import 'asset/asset_set.dart';
12 | import 'log.dart';
13 | import 'build_result.dart';
14 | import 'errors.dart';
15 | import 'graph/package_graph.dart';
16 | import 'package_provider.dart';
17 | import 'transformer/transformer.dart';
18 |
19 | /// A general-purpose asynchronous build dependency graph manager.
20 | ///
21 | /// It consumes source assets (including Dart files) in a set of packages,
22 | /// runs transformations on them, and then tracks which sources have been
23 | /// modified and which transformations need to be re-run.
24 | ///
25 | /// To do this, you give barback a [PackageProvider] which can yield a set of
26 | /// [Transformer]s and raw source [Asset]s. Then you tell it which input files
27 | /// have been added or modified by calling [updateSources]. Barback will
28 | /// automatically wire up the appropriate transformers to those inputs and
29 | /// start running them asynchronously. If a transformer produces outputs that
30 | /// can be consumed by other transformers, they will automatically be pipelined
31 | /// correctly.
32 | ///
33 | /// You can then request assets (either source or generated) by calling
34 | /// [getAssetById]. This will wait for any necessary transformations and then
35 | /// return the asset.
36 | ///
37 | /// When source files have been modified or removed, tell barback by calling
38 | /// [updateSources] and [removeSources] as appropriate. Barback will
39 | /// automatically track which transformations are affected by those changes and
40 | /// re-run them as needed.
41 | ///
42 | /// Barback tries to be resilient to errors since assets are often in an
43 | /// in-progress state. When errors occur, they will be captured and emitted on
44 | /// the [errors] stream.
45 | class Barback {
46 | /// The graph managed by this instance.
47 | final PackageGraph _graph;
48 |
49 | /// A stream that emits a [BuildResult] each time the build is completed,
50 | /// whether or not it succeeded.
51 | ///
52 | /// This will emit a result only once every package's [AssetCascade] has
53 | /// finished building.
54 | ///
55 | /// If an unexpected error in barback itself occurs, it will be emitted
56 | /// through this stream's error channel.
57 | Stream get results => _graph.results;
58 |
59 | /// A stream that emits any errors from the graph or the transformers.
60 | ///
61 | /// This emits errors as they're detected. If an error occurs in one part of
62 | /// the graph, unrelated parts will continue building.
63 | ///
64 | /// This will not emit programming errors from barback itself. Those will be
65 | /// emitted through the [results] stream's error channel.
66 | Stream get errors => _graph.errors;
67 |
68 | /// The stream of [LogEntry] objects used to report transformer log entries.
69 | ///
70 | /// If this stream has listeners, then log entries will go to that.
71 | /// Otherwise, a default logger will display them.
72 | Stream get log => _graph.log;
73 |
74 | Barback(PackageProvider provider) : _graph = new PackageGraph(provider);
75 |
76 | /// Gets the asset identified by [id].
77 | ///
78 | /// If [id] is for a generated or transformed asset, this will wait until
79 | /// it has been created and return it. If the asset cannot be found, throws
80 | /// [AssetNotFoundException].
81 | Future getAssetById(AssetId id) {
82 | return _graph.getAssetNode(id).then((node) {
83 | if (node == null) throw new AssetNotFoundException(id);
84 | return node.asset;
85 | });
86 | }
87 |
88 | /// Adds [sources] to the graph's known set of source assets.
89 | ///
90 | /// Begins applying any transforms that can consume any of the sources. If a
91 | /// given source is already known, it is considered modified and all
92 | /// transforms that use it will be re-applied.
93 | void updateSources(Iterable sources) =>
94 | _graph.updateSources(sources);
95 |
96 | /// Removes [removed] from the graph's known set of source assets.
97 | void removeSources(Iterable removed) =>
98 | _graph.removeSources(removed);
99 |
100 | /// Gets all output assets.
101 | ///
102 | /// If a build is currently in progress, waits until it completes. The
103 | /// returned future will complete with a [BarbackException] if the build is
104 | /// not successful.
105 | Future getAllAssets() => _graph.getAllAssets();
106 |
107 | /// Sets the transformer phases for [package]'s assets to [transformers].
108 | ///
109 | /// To the extent that [transformers] is similar to the previous transformer
110 | /// phases for [package], the existing asset graph will be preserved.
111 | ///
112 | /// Elements of the inner iterable of [transformers] must be [Transformer]s,
113 | /// [TransformerGroup]s, or [AggregateTransformer]s.
114 | void updateTransformers(String package, Iterable transformers) =>
115 | _graph.updateTransformers(package, transformers);
116 | }
117 |
--------------------------------------------------------------------------------
/test/asset_set_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.asset_set_test;
6 |
7 | import 'package:barback/barback.dart';
8 | import 'package:unittest/unittest.dart';
9 |
10 | import 'utils.dart';
11 |
12 | main() {
13 | initConfig();
14 |
15 | var fooId = new AssetId.parse("app|foo.txt");
16 | var barId = new AssetId.parse("app|bar.txt");
17 | var bazId = new AssetId.parse("app|baz.txt");
18 |
19 | group(".from()", () {
20 | test("creates a set from an iterable", () {
21 | var set = new AssetSet.from([
22 | new Asset.fromString(fooId, "foo"),
23 | new Asset.fromString(barId, "bar")
24 | ]);
25 |
26 | expect(set.containsId(fooId), isTrue);
27 | expect(set.containsId(barId), isTrue);
28 | expect(set.containsId(bazId), isFalse);
29 | });
30 | });
31 |
32 | group("[] operator", () {
33 | test("gets an asset with the given ID", () {
34 | var set = new AssetSet();
35 | var foo = new Asset.fromString(fooId, "foo");
36 | set.add(foo);
37 |
38 | expect(set[fooId], equals(foo));
39 | });
40 |
41 | test("returns null if no asset with the ID is in the set", () {
42 | var set = new AssetSet();
43 | expect(set[fooId], isNull);
44 | });
45 | });
46 |
47 | group(".add()", () {
48 | test("adds the asset to the set", () {
49 | var set = new AssetSet();
50 | var foo = new Asset.fromString(fooId, "foo");
51 | set.add(foo);
52 | expect(set.contains(foo), isTrue);
53 | });
54 |
55 | test("replaces a previously added asset with that ID", () {
56 | var set = new AssetSet();
57 | set.add(new Asset.fromString(fooId, "before"));
58 | set.add(new Asset.fromString(fooId, "after"));
59 | expect(set[fooId].readAsString(), completion(equals("after")));
60 | });
61 |
62 | test("returns the added item", () {
63 | var set = new AssetSet();
64 | var foo = new Asset.fromString(fooId, "foo");
65 | expect(set.add(foo), equals(foo));
66 | });
67 | });
68 |
69 | group(".addAll()", () {
70 | test("adds the assets to the set", () {
71 | var set = new AssetSet();
72 | var foo = new Asset.fromString(fooId, "foo");
73 | var bar = new Asset.fromString(barId, "bar");
74 | set.addAll([foo, bar]);
75 | expect(set.contains(foo), isTrue);
76 | expect(set.contains(bar), isTrue);
77 | });
78 |
79 | test("replaces assets earlier in the sequence with later ones", () {
80 | var set = new AssetSet();
81 | var foo1 = new Asset.fromString(fooId, "before");
82 | var foo2 = new Asset.fromString(fooId, "after");
83 | set.addAll([foo1, foo2]);
84 | expect(set[fooId].readAsString(), completion(equals("after")));
85 | });
86 | });
87 |
88 | group(".clear()", () {
89 | test("empties the set", () {
90 | var set = new AssetSet();
91 | var foo = new Asset.fromString(fooId, "foo");
92 | set.add(foo);
93 | set.clear();
94 |
95 | expect(set.length, equals(0));
96 | expect(set.contains(foo), isFalse);
97 | });
98 | });
99 |
100 | group(".contains()", () {
101 | test("returns true if the asset is in the set", () {
102 | var set = new AssetSet();
103 | var foo = new Asset.fromString(fooId, "foo");
104 | var bar = new Asset.fromString(barId, "bar");
105 | set.add(foo);
106 |
107 | expect(set.contains(foo), isTrue);
108 | expect(set.contains(bar), isFalse);
109 | });
110 | });
111 |
112 | group(".containsId()", () {
113 | test("returns true if an asset with the ID is in the set", () {
114 | var set = new AssetSet();
115 | var foo = new Asset.fromString(fooId, "foo");
116 | set.add(foo);
117 |
118 | expect(set.containsId(fooId), isTrue);
119 | expect(set.containsId(barId), isFalse);
120 | });
121 | });
122 |
123 | group(".removeId()", () {
124 | test("removes the asset with the ID from the set", () {
125 | var set = new AssetSet();
126 | var foo = new Asset.fromString(fooId, "foo");
127 | set.add(foo);
128 |
129 | set.removeId(fooId);
130 | expect(set.containsId(fooId), isFalse);
131 | });
132 |
133 | test("returns the removed asset", () {
134 | var set = new AssetSet();
135 | var foo = new Asset.fromString(fooId, "foo");
136 | set.add(foo);
137 |
138 | expect(set.removeId(fooId).readAsString(), completion(equals("foo")));
139 | });
140 |
141 | test("returns null when removing an asset not in the set", () {
142 | var set = new AssetSet();
143 | var foo = new Asset.fromString(fooId, "foo");
144 | set.add(foo);
145 |
146 | expect(set.removeId(barId), isNull);
147 | });
148 | });
149 |
150 | group(".ids", () {
151 | test("contains the ids of all the assets in the set", () {
152 | var set = new AssetSet();
153 | var foo = new Asset.fromString(fooId, "foo");
154 | var bar = new Asset.fromString(barId, "bar");
155 | set.addAll([foo, bar]);
156 | expect(set.ids, unorderedEquals([fooId, barId]));
157 | });
158 | });
159 | }
160 |
--------------------------------------------------------------------------------
/test/package_graph/transform/consume_input_test.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.test.package_graph.transform.pass_through_test;
6 |
7 | import 'package:scheduled_test/scheduled_test.dart';
8 |
9 | import '../../utils.dart';
10 |
11 | main() {
12 | initConfig();
13 | test("a transform consumes its input without overwriting it", () {
14 | initGraph([
15 | "app|foo.txt"
16 | ], {
17 | "app": [
18 | [new RewriteTransformer("txt", "out")..consumePrimary = true]
19 | ]
20 | });
21 |
22 | updateSources(["app|foo.txt"]);
23 | expectAsset("app|foo.out", "foo.out");
24 | expectNoAsset("app|foo.txt");
25 | buildShouldSucceed();
26 | });
27 |
28 | test("a transform consumes its input while a sibling overwrites it", () {
29 | initGraph([
30 | "app|foo.txt"
31 | ], {
32 | "app": [
33 | [
34 | new RewriteTransformer("txt", "out")..consumePrimary = true,
35 | new RewriteTransformer("txt", "txt")
36 | ]
37 | ]
38 | });
39 |
40 | updateSources(["app|foo.txt"]);
41 | expectAsset("app|foo.out", "foo.out");
42 | expectAsset("app|foo.txt", "foo.txt");
43 | buildShouldSucceed();
44 | });
45 |
46 | test("a transform stops consuming its input", () {
47 | initGraph({
48 | "app|foo.txt": "yes"
49 | }, {
50 | "app": [
51 | [new ConditionallyConsumePrimaryTransformer("txt", "out", "yes")]
52 | ]
53 | });
54 |
55 | updateSources(["app|foo.txt"]);
56 | expectAsset("app|foo.out", "yes.out");
57 | expectNoAsset("app|foo.txt");
58 | buildShouldSucceed();
59 |
60 | modifyAsset("app|foo.txt", "no");
61 | updateSources(["app|foo.txt"]);
62 | expectAsset("app|foo.out", "no.out");
63 | expectAsset("app|foo.txt", "no");
64 | buildShouldSucceed();
65 | });
66 |
67 | test("two sibling transforms both consume their input", () {
68 | initGraph([
69 | "app|foo.txt"
70 | ], {
71 | "app": [
72 | [
73 | new RewriteTransformer("txt", "one")..consumePrimary = true,
74 | new RewriteTransformer("txt", "two")..consumePrimary = true
75 | ]
76 | ]
77 | });
78 |
79 | updateSources(["app|foo.txt"]);
80 | expectAsset("app|foo.one", "foo.one");
81 | expectAsset("app|foo.two", "foo.two");
82 | expectNoAsset("app|foo.txt");
83 | buildShouldSucceed();
84 | });
85 |
86 | test(
87 | "a transform stops consuming its input but a sibling is still "
88 | "consuming it", () {
89 | initGraph({
90 | "app|foo.txt": "yes"
91 | }, {
92 | "app": [
93 | [
94 | new RewriteTransformer("txt", "one")..consumePrimary = true,
95 | new ConditionallyConsumePrimaryTransformer("txt", "two", "yes")
96 | ]
97 | ]
98 | });
99 |
100 | updateSources(["app|foo.txt"]);
101 | expectAsset("app|foo.one", "yes.one");
102 | expectAsset("app|foo.two", "yes.two");
103 | expectNoAsset("app|foo.txt");
104 | buildShouldSucceed();
105 |
106 | modifyAsset("app|foo.txt", "no");
107 | updateSources(["app|foo.txt"]);
108 | expectAsset("app|foo.one", "no.one");
109 | expectAsset("app|foo.two", "no.two");
110 | expectNoAsset("app|foo.txt");
111 | buildShouldSucceed();
112 | });
113 |
114 | test("a transform consumes its input and emits nothing", () {
115 | initGraph([
116 | "app|foo.txt"
117 | ], {
118 | "app": [
119 | [new EmitNothingTransformer("txt")..consumePrimary = true]
120 | ]
121 | });
122 |
123 | updateSources(["app|foo.txt"]);
124 | expectNoAsset("app|foo.txt");
125 | buildShouldSucceed();
126 | });
127 |
128 | test("a transform consumes its input, then is removed", () {
129 | initGraph([
130 | "app|foo.txt"
131 | ], {
132 | "app": [
133 | [new RewriteTransformer("txt", "out")..consumePrimary = true]
134 | ]
135 | });
136 |
137 | updateSources(["app|foo.txt"]);
138 | expectAsset("app|foo.out", "foo.out");
139 | expectNoAsset("app|foo.txt");
140 | buildShouldSucceed();
141 |
142 | updateTransformers("app", [[]]);
143 | expectNoAsset("app|foo.out");
144 | expectAsset("app|foo.txt", "foo");
145 | buildShouldSucceed();
146 | });
147 |
148 | test("a transform consumes its input and emits nothing, then is removed", () {
149 | initGraph([
150 | "app|foo.txt"
151 | ], {
152 | "app": [
153 | [new EmitNothingTransformer("txt")..consumePrimary = true]
154 | ]
155 | });
156 |
157 | updateSources(["app|foo.txt"]);
158 | expectNoAsset("app|foo.txt");
159 | buildShouldSucceed();
160 |
161 | updateTransformers("app", [[]]);
162 | expectAsset("app|foo.txt", "foo");
163 | buildShouldSucceed();
164 | });
165 |
166 | test("a transform which consumes its input is added", () {
167 | initGraph([
168 | "app|foo.txt"
169 | ], {
170 | "app": [[]]
171 | });
172 |
173 | updateSources(["app|foo.txt"]);
174 | expectNoAsset("app|foo.out");
175 | expectAsset("app|foo.txt", "foo");
176 | buildShouldSucceed();
177 |
178 | updateTransformers("app", [
179 | [new RewriteTransformer("txt", "out")..consumePrimary = true]
180 | ]);
181 | expectAsset("app|foo.out", "foo.out");
182 | expectNoAsset("app|foo.txt");
183 | buildShouldSucceed();
184 | });
185 | }
186 |
--------------------------------------------------------------------------------
/lib/src/graph/phase_forwarder.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.graph.phase_forwarder;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_node.dart';
10 | import '../asset/asset_node_set.dart';
11 |
12 | /// A class that takes care of forwarding assets within a phase.
13 | ///
14 | /// Each phase contains one or more channels that process its input assets. Each
15 | /// non-grouped transformer for that phase is a channel, each [TransformerGroup]
16 | /// in that phase is another, and the source node is the final channel. For each
17 | /// input asset, each channel individually decides whether to forward that asset
18 | /// based on whether that channel uses it. If a channel does decide to forward
19 | /// an asset, we call that forwarded asset an "intermediate forwarded asset" to
20 | /// distinguish it from the output of a [PhaseForwarder].
21 | ///
22 | /// All intermediate assets with a given origin are provided to a single
23 | /// [PhaseForwarder] via [addIntermediateAsset]. This forwarder then determines
24 | /// whether all channels in the phase produced intermediate assets. If so, that
25 | /// means the input asset wasn't consumed by any channel, so the
26 | /// [PhaseForwarder] forwards it again, producing an output which we'll call the
27 | /// "final forwarded asset".
28 | ///
29 | /// A final forwarded asset will be available only if all of the intermediate
30 | /// forwarded assets are themselves available. If any of the intermediate assets
31 | /// are dirty, the final asset will also be marked dirty.
32 | class PhaseForwarder {
33 | /// The number of channels to forward, counting the source node.
34 | int _numChannels;
35 |
36 | /// The intermediate forwarded assets.
37 | final _intermediateAssets = new AssetNodeSet();
38 |
39 | /// The final forwarded asset.
40 | ///
41 | /// This will be null if the asset is not being forwarded.
42 | AssetNode get output =>
43 | _outputController == null ? null : _outputController.node;
44 | AssetNodeController _outputController;
45 |
46 | /// A stream that emits an event whenever [this] starts producing a final
47 | /// forwarded asset.
48 | ///
49 | /// Whenever this stream emits an event, the value will be identical to
50 | /// [output].
51 | Stream get onAsset => _onAssetController.stream;
52 | final _onAssetController =
53 | new StreamController.broadcast(sync: true);
54 |
55 | /// Creates a phase forwarder forwarding nodes that come from [node] across
56 | /// [numTransformers] transformers and [numGroups] groups.
57 | ///
58 | /// [node] is passed in explicitly so that it can be forwarded if there are no
59 | /// other channels.
60 | PhaseForwarder(AssetNode node, int numTransformers, int numGroups)
61 | : _numChannels = numTransformers + numGroups + 1 {
62 | addIntermediateAsset(node);
63 | }
64 |
65 | /// Notify the forwarder that the number of transformer and group channels has
66 | /// changed.
67 | void updateTransformers(int numTransformers, int numGroups) {
68 | // Add one channel for the source node.
69 | _numChannels = numTransformers + numGroups + 1;
70 | _adjustOutput();
71 | }
72 |
73 | /// Adds an intermediate forwarded asset to [this].
74 | ///
75 | /// [asset] must have the same origin as all other intermediate forwarded
76 | /// assets.
77 | void addIntermediateAsset(AssetNode asset) {
78 | if (_intermediateAssets.isNotEmpty) {
79 | assert(asset.origin == _intermediateAssets.first.origin);
80 | }
81 |
82 | _intermediateAssets.add(asset);
83 | asset.onStateChange.listen((_) => _adjustOutput());
84 |
85 | _adjustOutput();
86 | }
87 |
88 | /// Mark this forwarder as removed.
89 | ///
90 | /// This will remove [output] if it exists.
91 | void remove() {
92 | if (_outputController != null) {
93 | _outputController.setRemoved();
94 | _outputController = null;
95 | }
96 | _onAssetController.close();
97 | }
98 |
99 | /// Adjusts [output] to ensure that it accurately reflects the current state
100 | /// of the intermediate forwarded assets.
101 | void _adjustOutput() {
102 | assert(_intermediateAssets.length <= _numChannels);
103 | assert(!_intermediateAssets.any((asset) => asset.state.isRemoved));
104 |
105 | // If there are any channels that haven't forwarded an intermediate asset,
106 | // we shouldn't forward a final asset. If we are currently, remove
107 | // it.
108 | if (_intermediateAssets.length < _numChannels) {
109 | if (_outputController == null) return;
110 | _outputController.setRemoved();
111 | _outputController = null;
112 | return;
113 | }
114 |
115 | // If there isn't a final asset being forwarded yet, we should forward one.
116 | // It should be dirty iff any of the intermediate assets are dirty.
117 | if (_outputController == null) {
118 | var finalAsset = _intermediateAssets.firstWhere(
119 | (asset) => asset.state.isDirty,
120 | orElse: () => _intermediateAssets.first);
121 | _outputController = new AssetNodeController.from(finalAsset);
122 | _onAssetController.add(output);
123 | return;
124 | }
125 |
126 | // If we're already forwarding a final asset, set it dirty iff any of the
127 | // intermediate assets are dirty.
128 | if (_intermediateAssets.any((asset) => asset.state.isDirty)) {
129 | if (!_outputController.node.state.isDirty) _outputController.setDirty();
130 | } else if (!_outputController.node.state.isAvailable) {
131 | _outputController.setAvailable(_intermediateAssets.first.asset);
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/lib/src/graph/transformer_classifier.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 | // for details. All rights reserved. Use of this source code is governed by a
3 | // BSD-style license that can be found in the LICENSE file.
4 |
5 | library barback.graph.transformer_classifier;
6 |
7 | import 'dart:async';
8 |
9 | import '../asset/asset_forwarder.dart';
10 | import '../asset/asset_node.dart';
11 | import '../errors.dart';
12 | import '../log.dart';
13 | import '../transformer/aggregate_transformer.dart';
14 | import '../transformer/wrapping_aggregate_transformer.dart';
15 | import 'node_status.dart';
16 | import 'node_streams.dart';
17 | import 'phase.dart';
18 | import 'transform_node.dart';
19 |
20 | /// A class for classifying the primary inputs for a transformer according to
21 | /// its [AggregateTransformer.classifyPrimary] method.
22 | ///
23 | /// This is also used for non-aggregate transformers; they're modeled as
24 | /// aggregate transformers that return the primary path if `isPrimary` is true
25 | /// and `null` if `isPrimary` is `null`.
26 | class TransformerClassifier {
27 | /// The containing [Phase].
28 | final Phase phase;
29 |
30 | /// The [AggregateTransformer] used to classify the inputs.
31 | final AggregateTransformer transformer;
32 |
33 | /// A string describing the location of [this] in the transformer graph.
34 | final String _location;
35 |
36 | /// The individual transforms for each classiciation key.
37 | final _transforms = new Map