OK I lied. Yesterday, I breezed past a little Dart problem in an effort to finish off some work. Today I try to address that oversight.
As I briefly mentioned yesterday, I am able to read Polymer configuration that is stored
<link>
imports inside the definition of a Dart Polymer:<script src="../../packages/browser/dart.js"></script> <script src="../../packages/browser/interop.js"></script> <polymer-element name="hello-you"> <link rel="import" href="hello-you.json"> <template> <!-- ... --> </template> <script type="application/dart" src="hello_you.dart"></script> </polymer-element>It takes a little effort, but I can grab
hello-you.json
from the Polymer, reading the data into the Polymer's configuration data. I have to work through the declaration property and run the result through a transformer, but it is doable.What I cannot quite do is
<link>
import data when I use the Polymer:<hello-you> <link rel="import" href="hello-you.json"> <p>And good-bye…</p> </hello-you>My problem is something of a running theme with Polymer.dart—the deployment transformer includes
<link>
imports inline, which results in actually seeing the JSON data embedded diring in the web page:I can work around this inside the Polymer definition with a deployment build transformer that searched for raw JSON, wrapping it in a
display: none
styled tag. The actual <link>
tag remains in the Polymer, allowing me to read it as configuration.My problem with configuration in a Polymer is twofold. First, it is a little harder to find the JSON to wrap it inside
display: none
tags. Second, even if I could do so, the <link>
tag is stripped out so that I could not read it even if I wanted to do so. I end up with this in the main web page:{ "hello": "Yo" } <div class="container"> <hello-you> <p>And good-bye…</p> </hello-you> </div>I think the best thing would be to pre-transform the page so that, by the time the Polymer transformer sees it, it is no longer a
<link>
. The pre-transformer could take the Polymer:<hello-you> <link rel="import" href="hello-you.json"> <p>And good-bye…</p> </hello-you>And transform it to:
<hello-you> <!-- link rel="import" href="hello-you.json" --> <p>And good-bye…</p> </hello-you>Then the code could pass through the Polymer transformer, which can do its usual thing—except that it will skip the commented
<link>
tag. Finally, a post-transformer could restore the original <link>
:<hello-you> <link rel="import" href="hello-you.json"> <p>And good-bye…</p> </hello-you>I have gotten pretty good at writing search and replace Dart Pub transformers. So good, in fact, that I think it is time to write a pub package. If I call the package
replace_transformer
, then my application could have a pubspec.yaml
that looks like:name: config_example dependencies: polymer: any replace_transformer: path: /home/chris/repos/replace_transformer dev_dependencies: unittest: any transformers: - replace_transformer: file: web/index.html match: '<link rel="import" href="hello-you.json">' replace: '<!-- link rel="import" href="hello-you.json" -->' - polymer: entry_points: web/index.html - replace_transformer: file: web/index.html match: '<!-- link rel="import" href="hello-you.json" -->' replace: '<link rel="import" href="hello-you.json">'In the
dependencies
section of pubspec.yaml
, I mark my new replace_transformer
package as a dependency. Once I am ready to release this package to pub.dartlang.org, I will remove the local path. For now, the local path allows me to build my transformer and try it right away. Brilliant!There are two ways to name a Dart transformer and I have forgotten one of them. The one I do remember is to create a file named
transformer.dart
. So in replace_transformer
, I create lib/transformer
:library replace.transformer; import 'dart:async'; import 'package:barback/barback.dart'; class ReplaceTransformer extends Transformer { String file; Pattern match; String replace; ReplaceTransformer(this.file, this.match, this.replace); FutureNothing out of the ordinary there. I declare a library. I import packages—async for access toisPrimary(Asset input) { /* ... */ } Future apply(Transform transform) { /* ... */ } }
Future
and barback
from pub itself for the Transformer
class as well as the ability to read the pubspec.yaml
configuration. I declare the three instance variables that will be assigned from pubspec.yaml
and define a constructor that assigns those instance variables. Lastly, I define the two methods that need to be implemented by a subclass of Transformer
. The isPrimary()
method indicates if a particular asset needs to be directly transformed (it will have to return true
when the asset matches file
). And the apply()
method actually performs the transformation.Pub expects its tranformers to define an
asPlugin
named constructor which access barback settings:class ReplaceTransformer extends Transformer { String file; Pattern match; String replace; ReplaceTransformer(this.file, this.match, this.replace); ReplaceTransformer.fromList(List a): this(a[0], a[1], a[2]); ReplaceTransformer.asPlugin(BarbackSettings settings) : this.fromList(_parseSettings(settings)); // ... }I redirect this constructor to the
fromList()
named constructor that takes a list and redirects to the primary constructor with the three expected values. The _parsedSettings()
is easy enough:List<String> _parseSettings(BarbackSettings settings) { var args = settings.configuration; return [ args['file'], args['match'], args['replace'] ]; }With that, I need to teach my
ReplaceTransfomer
when to apply transforms: Future<bool> isPrimary(Asset input) {
if (file == input.id.path) return new Future.value(true);
return new Future.value(false);
}
And I need to replace all instances of match
with replace
when the transform is applied: Future apply(Transform transform) {
var input = transform.primaryInput;
return transform.
readInputAsString(input.id).
then((html){
var fixed = html.replaceAllMapped(
match,
(m) => replace
);
transform.addOutput(new Asset.fromString(input.id, fixed));
});
}
And that actually does the trick. Now when I load my Polymer, the <link>
tag makes it through Polymer's transformer unscathed:Nice! I am still a little uncertain how to approach this stuff in Patterns in Polymer, especially since the JavaScript version of Polymer does not really have the concept of asset packaging. But, for now, I am glad to feel like I have a workable, if a bit verbose solution in Dart.
Day #992
Hey I heard about Polymer JS Vulcanize, which is, by the looks of things, doing a similar job to pub build. Just curious if you've heard of it and if you going to have to use a similar strategy in Polymer JS, to what you're doing with Dart Transformers here, as a result of what Vulcanizer does? http://www.polymer-project.org/tooling-strategy.html#vulcanize-build-tool
ReplyDeleteD'oh! You're right, of course. I'll check it out tonight -- before the #pairwithme session :)
DeleteThanks for the reminder!