Sunday, April 6, 2014

Vulcanize Inline… Into Polymer.dart?

Up tonight, a bit of Polymer deployment sanity checking. Does the approach that I have taken in polymer-one-script for deployment of Polymer.dart elements make sense?

I have enjoyed exploring the issue of deploying Dart Polymer element the past few days, but I begin to realize that it may not wind up being relevant in Patterns in Polymer. In that book, I am trying to find patterns and approaches that work well in both the JavaScript and Dart versions of the project. That is, I hope to identify patterns that transcend individual language implementations and instead cut to the core of what it means to be a Polymer element.

Herein lies my problem: deployment of JavaScript Polymers is trivial thanks to Vulcanize. I cannot very well write a “transcendent” chapter in the two different versions of the book where it takes me 20 pages to describe a thing in one language and one paragraph in the other. That is hardly transcendent.

Still, it seems worth taking a look. The default vulcanize command indeed does not produce what I have polymer-one-script doing in Dart. It produces a deployable HTML description of the element with external <script> references:
➜  js git:(master) ✗ vulcanize elements/x-pizza.html       
Default output to vulcanized.html in the input directory.
➜  js git:(master) ✗ grep src elements/vulcanized.html 
<script src="../bower_components/polymer/polymer.js"></script>
  <script src="x_pizza_toppings.js"></script>
  <script src="x_pizza.js"></script>
It does, however, support the --inline option to accomplish more or less (actually much less size-wise) the same thing that polymer-one-script does:
➜  js git:(master) ✗ vulcanize --inline elements/x-pizza.html
Default output to vulcanized.html in the input directory.
➜  js git:(master) ✗ grep src elements/vulcanized.html       
➜  js git:(master) ✗ 
Actually, that is not quite the same thing that polymer-one-script does. My script produces a JavaScript source file for the element instead of an HTML description of the element with the code embedded in it. Ah well, I am not going to sweat that difference much.

With that question answered, I ought to call it a night, but… Darn it I am curious about something else now. The example on which I have been experimenting, <x-pizza>, is actually comprised of two elements: the parent <x-pizza> and several child <x-pizza-toppings> elements that enable the discerning pizza buyer to add toppings:

This is the same in both the Dart and JavaScript versions of my element. I wonder, can I create a single, inline version of the <x-pizza-toppings> element in JavaScript and use it in Dart? There is one way to find out!
➜  js git:(master) ✗ vulcanize --inline elements/x-pizza-toppings.html 
Default output to vulcanized.html in the input directory.
➜  js git:(master) ✗ cd ../dart      
➜  dart git:(master) ✗ cp ../js/elements/vulcanized.html lib/elements
Giving this a try with the pub serve built-in web server and Dartium, I find… an error:
Uncaught ReferenceError: PolymerExpressions is not defined
That would seem to indicate that I need some lovely JavaScript libraries, which make me think that this is going to wind up being prohibitively hard. Still, I give it a shot by copying the Bower configuration from my JavaScript sample and installing Polymer.js into my Polymer.dart application:
➜  web git:(master) ✗ cp ../../js/bower.json .
➜  web git:(master) ✗ bower install
After adding that to my containing page, however. I still get failures. I add the platform as:
<script src="bower_components/platform/platform.js"></script>
That ought to load the core of Polymer.JS—enough, at least, to define PolymerExpressions and anything else needed. Unfortunately, this seems to interact very badly with the JavaScript coming from pub build as I get crazy long error messages:
TypeError {stack: "TypeError: Cannot read property 'getAttribute' of …0/bower_components/platform/platform.js:35:22390)", message: "Cannot read property 'getAttribute' of undefined"}
 "TypeError: Cannot read property 'getAttribute' of undefined
    at eval [as getAttribute] (eval at getMethod (http://localhost:8080/packages/shadow_dom/shadow_dom.debug.js:1796:49), <anonymous>:2:17)
    at d.createdCallback (http://localhost:8080/index.html:85:22502)
    at k (http://localhost:8080/bower_components/platform/platform.js:35:27681)
    at h (http://localhost:8080/bower_components/platform/platform.js:35:27258)
    at Object.s [as upgrade] (http://localhost:8080/bower_components/platform/platform.js:35:28650)
    at h (http://localhost:8080/bower_components/platform/platform.js:35:22897)
    at e (http://localhost:8080/bower_components/platform/platform.js:35:22605)
    at http://localhost:8080/bower_components/platform/platform.js:35:22667
    at http://localhost:8080/bower_components/platform/platform.js:35:22555
    at b (http://localhost:8080/bower_components/platform/platform.js:35:22390)" shadow_dom.debug.js:2816
TypeError {stack: "TypeError: Cannot read property 'polymerShadowRoot…0/bower_components/platform/platform.js:35:25116)", message: "Cannot read property 'polymerShadowRoot_' of undefined"}
 "TypeError: Cannot read property 'polymerShadowRoot_' of undefined
    at shadowRoot (http://localhost:8080/packages/shadow_dom/shadow_dom.debug.js:4345:23)
    at c (http://localhost:8080/bower_components/platform/platform.js:35:22474)
    at http://localhost:8080/bower_components/platform/platform.js:35:22568
    at b (http://localhost:8080/bower_components/platform/platform.js:35:22390)
    at b (http://localhost:8080/bower_components/platform/platform.js:35:22403)
    at b (http://localhost:8080/bower_components/platform/platform.js:35:22403)
    at d (http://localhost:8080/bower_components/platform/platform.js:35:22532)
    at f (http://localhost:8080/bower_components/platform/platform.js:35:22644)
    at g (http://localhost:8080/bower_components/platform/platform.js:35:22711)
    at Object.y [as upgradeDocument] (http://localhost:8080/bower_components/platform/platform.js:35:25116)"
I suppose I should have left well enough alone, but this seems like something that might be nice. I may give this another try tomorrow—with the dev channel version of Dart and Polymer.dart.

Day #26


  1. Make sure you try it with Polymer Dart version 0.10.0-pre.7, as my imperfect memory says it should support, or is planning on supporting, the use of JavaScript Polymer elements directly.

    1. Thanks for the tip. I'll give it a try today :)

    2. 20 pages to describe this in Dart and just one line of Vulcanize to describe it in javascript?
      Something must be wrong with PolymerDart if you cannot write a Dart version of the "Vulcanizer" tool for it!

    3. “Wrong” is certainly the wrong word for it :P

      Like the JavaScript version of the project, Polymer.dart is very much an active work-in-progress. The Dart project is playing a bit of catch-up with the JavaScript project so they have to pick and choose what the most important features are to get done first. In my experience, they have done an admirable job of making those choices.

      The default implementation for Polymer.dart's build system produces reasonable JavaScript for an _application_. That is, if you include a Polymer in an application, you can very easily (easier than Vulcanize) build a version of that application that is rock solid on the modern web.

      I spent the last week or so understanding why vulcanize --inline is not a trivial thing to implement in Polymer.dart. I still have every confidence that the Polymer.dart team will figure it out. Until then, polymer-one-script is a reasonable first pass at just that. It's a bit flaky right now (since I wrote it!), but it too would be a one paragraph chapter -- so not worth it.

      Anyhow, I would object to categorizing anything about Polymer.dart as “wrong.” It's a work-in-progress and very much headed in the right direction :)