Sunday, May 5, 2013

Thinking About Dart Pub Code Organization

‹prev | My Chain | next›

The package layout conventions for Dart Pub suggest putting web content in the web sub-directory of packages. Having worked on a Dart package that heavily relies on web content, I have to respectfully disagree with that advice. To be sure, Dart and pub are new and undergoing rapid change so our disagreement may not last long. Further, this is a very new Dart package, so I could very easily have some nasty lessons in store if I keep down this path.

That said, I plan to delete the web sub-directory from the ICE Code Editor package:
➜  ice-code-editor git:(master) ✗ tree -d -I out        
.
├── example
│   ├── packages -> ../packages
│   └── public
│       └── packages -> ../../packages
├── lib
│   ├── css
│   ├── html
│   └── js
│       └── ace
├── packages
│   ├── browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.5.0+1/lib
│   ├── ice_code_editor -> ../lib
│   ├── js -> /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.22/lib
│   ├── meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.5.0+1/lib
│   └── unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.4.0/lib
├── test
│   └── packages -> ../packages
└── web
    ├── css
    │   └── packages -> ../../packages
    ├── html
    │   └── packages -> ../../packages
    └── packages -> ../packages
The problem with the web sub-directory is that is it not easily available outside of the package. As can be seen in the packagaes sub-directory, pub links in only the lib sub-directory of a package. The example, test, and web sub-directories are in the pub cache, but not in the package directory itself.

Instead, I plan to use css, html, and js sub-directories inside of lib along with all of my Dart code. This allows me to reference web resources from outside of the ICE package as packages/ice_code_editor/css, packages/ice_code_editor/html, and packages/ice_code_editor/js. Because pub symlinks the current package into its own packages sub-directory, I can even reference these resources from within the package using the same path:
    var script = new ScriptElement();
    script.src = "packages/ice_code_editor/js/ace/ace.js";
    document.head.nodes.add(script);
Today, I need to add CSS in addition to the in-code styling that I have been doing. Since the CSS file is in the lib sub-directory, I can expect any context using this code to work with the packages/ice_code_editor/css path. So, I can automatically apply styles that are bundled right in my pub package with a new LinkElement:
  _applyStyles() {
    var style = new LinkElement()
      ..type = "text/css"
      ..rel = "stylesheet"
      ..href = "packages/ice_code_editor/css/ice.css";
    document.head.nodes.add(style);
  }
(the double dots are method cascades)

I can even make use of the lib sub-directory in my Dart package to hold fonts for CSS. I add the very lovely inconsolatas font in lib/fonts and can then pull it in from lib/css/ice.css:
@font-face {
  font-family: 'inconsolata';
  src: url('../fonts/inconsolata.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}

.ice-code-editor-editor .ace_content {
  font-family: inconsolata;
}
With that, I have everything styled nicely, highlighted and even using a pretty font:



I cannot adequately describe how cool this is. With a single pub install, all of the Dart code necessary to run the ICE Code Editor will be installed—dependencies and all. Not only that, but all of the JavaScript, CSS, font files, and HTML necessary to run the editor will also be installed. All of it will be readily accessible from outside of my library, but it will all be a black box to the Dart developer. All of that Dart and everything else starts up with just:
<head>
  <script src="packages/browser/dart.js"></script>
  <script type="application/dart">
    import 'package:ice_code_editor/editor.dart' as ICE;

    main() {
      var ice = new ICE.Editor('#ice');

      ice.content = '''
        //  Code for the editor goes here...
      ''';
    }
  </script>
</head>
<div style="width:600px; height: 400px" id="ice"></div>
For a complex web app, that's nothing short of amazing.

Day #742

4 comments:

  1. There's this distinction between "application packages" (applications) and "library packages" (bits and pieces that are meant to be reused by other packages). For library packages, it totally makes sense to put your web content into "lib" -- the "web" directory only makes sense in application packages.

    ReplyDelete
    Replies
    1. Ah! Application packages! I could not for the life of me figure out when web would ever be of any use. But that makes perfect sense. Thanks for cluing me in :)

      Delete
  2. Hey, Chris. You're exactly right that you can't reuse anything from a package if it isn't in lib. That's a bug. :)

    In fact: https://code.google.com/p/dart/issues/detail?id=6101

    Right now, jamming your resources inside lib is the best you can do, but we're working on solving that pretty much right now. This is just a corner of the package experience that isn't implemented yet. We'll get there.

    ReplyDelete
    Replies
    1. Phew! Glad I wasn't missing something. Issue starred. Hopefully I can be first in line to test the eventual implementation :)

      Delete