Sunday, August 25, 2013

I Got Served… at Dart Pub

Last week on the Dart mailing list, I learned that words like "pants" and "scissors" that have no singular form are called "pluralia tantum." The exquisite Bob Nystrom laid that one on us along with the news that Dart Pub supports both assets and bundles its own web server. Wha?

Saaay. I could use both of those things. In the ICE Code Editor, we have a bunch of example files that we serve with a very simple app.dart server that is also included in there:
➜  example git:(master) pwd
➜  example git:(master) tree -I '*.deps|*.map'
├── app.dart
├── packages -> ../packages
└── public
    ├── events.html
    ├── full.dart
    ├── full.dart.js
    ├── full.html
    ├── index.html
    ├── main.dart
    ├── main.dart.js
    └── packages -> ../../packages

3 directories, 8 files
Can I move the examples in the public subdirectory into the new asset top-level directory and pub serve them?
➜  example git:(master) cd ..
➜  ice-code-editor git:(master) mkdir asset
➜  ice-code-editor git:(master) mv example/public asset/examples
➜  ice-code-editor git:(master) ✗ pub serve
Serving ice_code_editor on http://localhost:8080
Build completed successfully

# Meanwhile, in another screen...
➜  ice-code-editor git:(master) ✗ curl http://localhost:8080/assets/ice_code_editor/examples/full.html
  <script src="packages/browser/dart.js"></script>
  <script src="packages/browser/interop.js"></script>
  <script src="full.dart" type="application/dart"></script>
Yes I can.

That is much easier than telling folks to spin up the custom app.dart server! But now that I have that working, I realize that my runnable examples are probably better placed in the web subdirectory:
➜  ice-code-editor git:(master) ✗ mkdir web
➜  ice-code-editor git:(master) ✗ mv asset/examples/* web/
➜  ice-code-editor git:(master) ✗ pub serve
Serving ice_code_editor on http://localhost:8080
Build completed successfully
With the full.html and other example pages now in the web subdirectory, I can access them directly at the root of pub serve:
➜  ice-code-editor git:(master) ✗ curl http://localhost:8080/full.html
  <script src="packages/browser/dart.js"></script>
  <script src="packages/browser/interop.js"></script>
  <script src="full.dart" type="application/dart"></script>
That is pretty freaking cool.

The asset top-level directory is meant for other kinds of resources. I have been jamming the CSS, HTML, and JavaScript that are needed by ICE directly into the lib top-level directory:
➜  ice-code-editor git:(master) ✗ tree lib -I '*\.dart' 
├── css
│   └── ice.css
├── fonts
│   └── inconsolata.woff
├── full
├── html
│   └── preview_frame.html
└── js
    ├── ace
    │   ├── ace.js
    │   ├── ext-searchbox.js
    │   ├── keybinding-emacs.js
    │   ├── mode-css.js
    │   ├── mode-html.js
    │   ├── mode-javascript.js
    │   ├── theme-chrome.js
    │   ├── theme-textmate.js
    │   └── worker-javascript.js
    └── deflate
        ├── rawdeflate.js
        └── rawinflate.js

7 directories, 14 files
I need them in lib so that, when the ice_code_editor packages is installed, the web server will have access to them. Before these recent changes to Dart Pub, only files in lib were visible outside of a package. This seems to be changing, but is is ready for my needs yet? There is one way to find out…

I move all of those non-dart assets into the asset top-level directory:
➜  ice-code-editor git:(master) ✗ cd lib
➜  lib git:(master) ✗ mv css fonts html js ../asset/
Then, I go through my code to replace any instance of packages/ice_code_editor/<...> with assets/ice_code_editor/<...>:
  static _attachScripts() {
    if (_scripts != null) return;

    var script_paths = [

    var scripts = script_paths.
      map((path) { /* ... */ });
    return _scripts = scripts;
And, amazingly that Just Works™:

I am getting excited here! My codebase is all kinds of cleaner because of this. I no longer need the app.dart mini-server. My example pages are still contained in a reasonable place. My CSS, JavaScript, and HTML—my assets—are no longer cluttering up the lib directory of my package. Now lib contains only Dart code and I have a working, well-named placed for assets to go.

Of course...

This is not the real use-case for placing my assets in lib. The reason that all of those assets were in there was so that that they could be visible when served from a static web server (on GitHub pages). And there, I think that my luck runs out.

If I update the pubspec.yaml for the, then I see no assets directory exposed:
➜  ice-beta git:(gh-pages) ✗ gd .
diff --git a/ice-beta/pubspec.yaml b/ice-beta/pubspec.yaml
index 1a87698..61c8f7a 100644
--- a/ice-beta/pubspec.yaml
+++ b/ice-beta/pubspec.yaml
@@ -4,4 +4,5 @@ description: Full screen ICE
 author: Chris Strom 
-  ice_code_editor: any
+  ice_code_editor:
+    path: /home/chris/repos/ice-code-editor
➜  ice-beta git:(gh-pages) ✗ pub update
Resolving dependencies..................
Downloading browser 0.6.19 from hosted...
Downloading path 0.6.19 from hosted...
Downloading meta 0.6.19 from hosted...
Downloading stack_trace 0.6.19 from hosted...
Dependencies updated!
➜  ice-beta git:(gh-pages) ✗ ls packages/ice_code_editor
editor.dart  full  full.dart  gzip.dart  ice.dart  store.dart
Before, when my assets where in the lib directory, they were linked into ice-beta along with the Dart source code. Now, they are nowhere to be found.

That said…

Installing pub packages create symbolic links in the packages directory:
➜  ice-beta git:(gh-pages) ✗ ls -l packages 
total 32
lrwxrwxrwx 1 chris chris 65 Aug 25 21:21 browser -> /home/chris/.pub-cache/hosted/
lrwxrwxrwx 1 chris chris 66 Aug 25 21:21 crypto -> /home/chris/.pub-cache/hosted/
lrwxrwxrwx 1 chris chris 69 Aug 25 21:21 ctrl_alt_foo -> /home/chris/.pub-cache/hosted/
lrwxrwxrwx 1 chris chris 37 Aug 25 21:21 ice_code_editor -> /home/chris/repos/ice-code-editor/lib
lrwxrwxrwx 1 chris chris 60 Aug 25 21:21 js -> /home/chris/.pub-cache/hosted/
lrwxrwxrwx 1 chris chris 62 Aug 25 21:21 meta -> /home/chris/.pub-cache/hosted/
lrwxrwxrwx 1 chris chris 62 Aug 25 21:21 path -> /home/chris/.pub-cache/hosted/
lrwxrwxrwx 1 chris chris 69 Aug 25 21:21 stack_trace -> /home/chris/.pub-cache/hosted/
lrwxrwxrwx 1 chris chris 68 Aug 25 21:21 unittest -> /home/chris/.pub-cache/hosted/
Symbolic links are a no-no on Jekyll/GitHub pages, so I use a Bash script to de-cache them (the current built-in pub actions do this, but only in other use-cases). If I am already doing that, might I also check for the existence of an asset directory in each package and install that as well?

By way of proof-of-concept, I manually copy the assets from ICE into the GitHub pages ice-beta site:
➜  ice-beta git:(gh-pages) ✗ mkdir -p assets/ice_code_editor
➜  ice-beta git:(gh-pages) ✗ cp -r ~/repos/ice-code-editor/asset/* assets/ice_code_editor/
After decaching the packages, I fire up a local jekyll server:
➜  ice-beta git:(gh-pages) ✗ cd ..
➜  gamingjs git:(gh-pages) ✗ jekyll --auto --server
Configuration from /home/chris/repos/gamingjs/_config.yml
Auto-regenerating enabled: /home/chris/repos/gamingjs -> /home/chris/repos/gamingjs/_site
[2013-08-25 21:38:46] regeneration: 492 files changed
[2013-08-25 21:38:46] INFO  WEBrick 1.3.1
[2013-08-25 21:38:46] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
[2013-08-25 21:38:46] INFO  WEBrick::HTTPServer#start: pid=9822 port=4000

It works!

There are no network errors in the Developer Console and the ICE Code Editor looks to be running perfectly well. It is fairly encouraging that it was so easy to adapt the new assets strategy to my GitHub pages sites.

Even so, this is not a fully baked solution. Some of the work that we have been doing in ICE recently is to allow for more re-use. It does no good to make it easier for other folks to use ICE only to implement an assets solution that requires some post-processing. I may include an internal tool for doing just that. Or I may stick with the web directory only solution for now and await future changes in Pub.

Regardless, I am eager for the next improvements in Pub. And hope that the next announcement will come with similarly useful tidbits of information.

Day #854

No comments:

Post a Comment