Tuesday, April 1, 2014

Can't Manually Dart2Js a Polymer


Tonight, I compile Polymer.dart transform assets. No excuses.

I am still trying to compile Polymer code inside a Dart Pub transformer because the default dart2js compilation that pub build does occurs too late for my transformer to use. Without the dart2js output, I cannot compile everything into a single deploy script instead of the multiple JS scripts that are currently created by the built-in Polymer transformer.

I ended last night thinking that I was relatively close. I had written a transform asset to the filesystem, forked a dart2js process, only to find that I needed at least one other asset to successfully compile. Tonight, I add that asset—the generated index.html.0.dart—to the list of files that need to be temporarily available to the transformer:
class SingleJsPolymerTransformer extends Transformer {
  List<AssetId> bootstrappedPolymerElements = [];
  // ...
  Future<bool> isPrimary(Asset input) {
    if (input.id.path.contains('html_bootstrap.dart')) {
      bootstrappedPolymerElements.add(input.id);
    }
    if (input.id.path.contains('html.0.dart')) {
      bootstrappedPolymerElements.add(input.id);
    }
    // ...
  }
  // ...
}
Assets are Future based, which means that I need a way to wait for any and all temporary files to be written to the file system. To make that happen, I write a method that returns a list of Futures that will complete when their files have been written:
  List<Future> writeTemporaryAssets(transform, List<AssetId> assets) {
    return assets.
      map((asset){
        return transform.
          readInputAsString(asset).
          then((code) {
            new File(asset.path).
              openSync(mode: FileMode.WRITE).
              writeStringSync(code);
          });
      });
  }
The return value of the innermost then() is itself a future (the then() method always returns a future), so I have successfully generated a list of Futures, each of which complete when the file has been written to the filesystem. Now I need to wait for all of those Futures to complete before attempting to dart2js compile my Polymer code. This is what Future.wait() does:
  buildSingleJavaScript(templateHtml, transform) {
    return Future.
      wait(writeTemporaryAssets(transform, bootstrappedPolymerElements)).
      then((_){
        var result = Process.runSync('dart2js', [ /* ... */]);
        // Assemble dart2js and various library code...
      });
  }
Given a list of writeTemporaryAssets() futures, Future.wait() returns another Future that will complete only when every future in the list has completed. In other words, I am now waiting until all temporary assets are available on the filesystem before running dart2js. Perfect!

Naturally, this still does not work. Actually, writing the temporary assets works as does compiling. Unfortunately, compiling these temporary assets does not generate the same code that the dart2js pub transformer generated. It is close, but a diff of my dart2js compiled JavaScript with that generated by the pub transformer reveals numerous differences, including:
...
 C.Ka=new H.GD("call")
+C.Je=new H.GD("current")
 C.nN=new H.GD("dynamic")
 C.AZ=new H.GD("dart.core.String")
+C.Kl=new H.GD("ingredients")
 C.Wn=new H.GD("length")a
 C.PC=new H.GD("dart.core.int")
+C.YS=new H.GD("name")
 C.OV=new H.GD("noSuchMethod")
+C.aC=new H.GD("pizzaState")
 C.c8=new H.GD("registerCallback")
 C.ok=new H.GD("dart.core.Null")
...
Unfortunately, the missing objects look to be annotated Polymer attributes:
@CustomTag('x-pizza-toppings')
class XPizzaToppings extends PolymerElement {
  @published String name;
  @published List ingredients = [];
  @observable String current = '';
  // ...
}
Without those observed and published attributes, Polymer code isn't really Polymer code. And, even though there are no errors when I fire up the code, my <x-pizza> builder does not work. It does not even have ingredients:



Darn.

One the one hand, this was a terribly ugly approach, so it is not too much a shame that it will not work. On the other hand, why can't I compile annotated code with dart2js? Given that this approach is such an ugly mess, I likely will not try to answer that. Instead, I will probably take a closer look a the dart2js and Polymer.dart transformers tomorrow to see if I have any luck in there. And if not, I can always fall back to shell scripts.


Day #21

1 comment:

  1. Is there a way to detect when dart2js has finished, then apply the transform in a future???

    ReplyDelete