Automatically refresh page when Dart source files change

advertisements

How can you make Dartium automatically reload your web client application whenever a change is made to the source files?

Related: How do I make Firefox auto-refresh on file change?


EDIT: You can also skip all this and simply hit CTRL+R in the Editor. The script below may still be useful if you're using tooling outside of the Dart Editor (but are still relying on it for the build process) or want to code-and-preview without focus shifting to the Dartium window.

Cut out keystrokes and automate your monkeys!

This technique uses dart.build to "touch" your HTML file whenever you make a change to your project, then relies on the LivePage extension to refresh it in the browser.

  1. Fire up Dartium and install the LivePage extension. (Settings | Extensions | Get more extensions | LivePage from www.fullondesign.co.uk | Add to chrome)

  2. Run your project. While viewing your page in Dartium, click the LivePage icon. A red "Live" overlay will appear. This means LivePage is watching the html file and its assets (e.g. css file) for changes.

  3. Test, by making a quick change to your html file and saving it. The page in Dartium should update.

  4. Create a build.dart file in the same directory as your project's pubspec.yaml. The Dart Editor will run this file whenever you make a change in your project (e.g. when you save changes to any of your .dart files).

  5. Put the code below in build.dart. Update 'web/yourpage.html' to point to the HTML file being monitored by LivePage.

  6. Now change one of your .dart files, save it, and watch the magic unfold.

In short: Save code ▶ Dart Editor triggers build.dart ▶ touches html file ▶ LivePage refreshes Dartium

import 'dart:io';

// This number needs to be high enough to prevent the Dart Editor from going
// into an "infinite compile" loop.  If that happens, simply comment out the
// call to touch() below and save this file.
const int MIN_INTERVAL_MS = 5000;
const String HTML_FILE = 'web/yourpage.html';

void main() {
  build(new Options().arguments, [HTML_FILE]);
  touch(HTML_FILE, new Duration(milliseconds:MIN_INTERVAL_MS));
}

/// Save a small, trivial change to the contents of [filename], unless
/// its already been modified within the last [interval].
void touch(String filename, [Duration interval]) {
  const int SPACE = 32;
  var file = new File(filename);
  if (?interval &&
      new Date.now()
      .difference(file.lastModifiedSync())
      .inMilliseconds < interval.inMilliseconds) return;
  RandomAccessFile f = file.openSync(FileMode.APPEND);
  try {
    // If the file doesn't end with a space, append one, otherwise remove it
    int length = f.lengthSync();
    f.setPositionSync(length - 1);
    if (f.readByteSync() == SPACE) {
      f.truncateSync(length - 1);
    } else {
      f.writeByteSync(SPACE);
    }
  } finally {
    f.closeSync();
  }
}

If you need to troubleshoot, you can run dart build.dart from a command line.

The touch() function either appends or removes a trailing space at the end of the file. Note LivePage doesn't seem to do anything if all you change is the modified date.

Because the Dart Editor is always monitoring your files, it will pick up the change made by build.dart, go "Hey, this project just changed" and call build.dart again... and again... and again. To avoid infinite loops, the script only touches the file if its been stale for at least MIN_INTERVAL_MS.

LivePage has a feature that unobtrusively "re-injects" CSS and Javascript snippets when they change, without forcing a refresh of the entire page. Unfortunately the brute force technique used here (i.e. overwriting the html file) overrides that behavior.

The Dart folks also provide a web_ui page that talks about tooling, although note you don't actually need to install the web_ui package for build.dart to work.