Thursday, April 3, 2014

Command Line Dart to Produce a Single JS Polymer


Up tonight, I try my hand at a Dart command-line script to extract a Dart Pub built Polymer element into a single-file JavaScript for deployment.

I hate to do this, but my efforts at creating a transformer simply ran into a timebox limit past which I am unwilling to invest time (at least for now). I am tempted to implement this as a Bash shell script as I already know the commands that can be used to accomplish this manually. Still, I might as well give this a shot—I have not written many Dart command-line scripts.

I start with a simple skeleton in my application's bin directory:
#!/usr/bin/env dart

import 'dart:io';

main() {
  print('yo');
}
I save that as bin/reduce.dart, make it executable, and run it:
➜  dart git:(master) ✗ chmod 775 bin/reduce.dart 
➜  dart git:(master) ✗ ./bin/reduce.dart
yo
Easy peasy. Now for the more involved part.

And I'll admit here that my motivation for using Dart here wasn't entirely motivated from a desire to learn. At least some of the code that I have written in my transformer for the past few days can be used here. The code that strips <script> tags, extracts <polymer-template> elements, and builds dynamic JavaScript that recreated the <polymer-template> elements can all be used in reduce.dart.

In fact, that goes extremely smoothly. For the most part, I remove references to Trasnformer objects and I replace Asset code with File writes. The main() entry point into my reduce script winds up with two part. One to strip the Polymer entry point web page of its Polymer code:
main() {
  // Strip entry point of polymer-templates and script tags
  var html = new File('build/$filename').readAsStringSync();

  var fixed = html.
    replaceAllMapped(
      polymerElementRegExp,
      (m) { polymer_template = m.group(0); return ''; }
    ).
    replaceAll(new RegExp(r'<script[^<]+</script>'), '').
    replaceFirst('<head>', '<head>\n<script src="scripts/deploy.js"></script>');

  new File('deploy/index.html').
    openSync(mode: FileMode.WRITE).
    writeStringSync(fixed);

  // Build Single JS file
  // ...
}
And the other to combine various assets into a single JavaScript file:
main() {
  // Strip entry point of polymer-templates and script tags
  // ...

  // Build Single JS file
  var js = buildSingleJavaScript(polymer_template);
  new File('deploy/scripts/deploy.js').
    openSync(mode: FileMode.WRITE).
    writeStringSync(js);
}
The regular expression to extract <polymer-element> templates and the buildSingleJavaScript() method both come from my previous efforts. Thankfully, they work!

When I run a simple web server from the deploy directory (which is where this script currently dumps its work), I find that the Polymer runs just fine in non-Dart browsers:



And the smoke test web page now includes only a single <script> tag:
<!DOCTYPE html><html lang="en">
  <head>
    <script src="scripts/deploy.js"></script>
    <title>Pizza Maker</title>
    <meta content="text/html; charset=UTF-8" http-equiv="content-type">
    <link type="text/css" rel="stylesheet" href="/assets/deployment_experiment/bootstrap.min.css">
  </head>
  <body>
    <div class="container">
      <h1>Ye Olde Dart Pizza Shoppe</h1>
      <x-pizza></x-pizza>
    </div>
  </body>
</html>
I still feel bad about my inability to get the pub transformer to work, but wow, this was much easier. The transformer is probably the right way to go about it in the (hopefully not too distant) future. But for today, this is definitely the way to go.


Day #23

5 comments:

  1. Nah don't fill bad, I've been following this transformer series with interest and what you've done with the command line seems reasonable.

    ReplyDelete
    Replies
    1. Don't *feel* bad. Also... pro-tip... don't comment on blogs when you're tired :/ :)

      Delete
    2. Cool. I don't feel *too* bad after making an obsessive go at the transformer. If nothing else, I have a _very_ good understanding now about why we don't have a transformer built-in yet :)

      Delete
  2. There's also a library for Dart called hop, that might fit what you're trying to do as well. Then what you've done won't be that much improved by using hop, IMO

    ReplyDelete
    Replies
    1. Hunh. I'd forgotten about hop. But agree that, although it might be a good fit, it doesn't add a ton in this case.

      Delete