Skip to content
This repository has been archived by the owner on Dec 19, 2017. It is now read-only.

Initialization from HTML

Kathy Walrath edited this page Sep 15, 2015 · 4 revisions

This document presents how to support static initialization of code loaded from HTML documents in Dart script tags and HTML imports. This document is part of a larger design to restructure how we consume custom elements in Dart.

Background

Static initializers play an essential role with web components and custom element registration. We decided to design static initializers in two layers. A first layer is focused in how to support it in Dart standalone (see design doc). While a second layer (this document), describes how we support HTML documents, multiple Dart script tags, and HTML imports. The Dart static initialization design doc includes a more detailed discussion of our motivation behind this design.

Design

We propose adding a new library html_init.dart which exposes a top-level function htmlInit.run(). This function will crawl the HTML document of the application and invoke the standalone initialization init.run() on each reachable Dart script tag (including HTML imports). HTML imports are visited in a post-order fashion.

Note: the location of this library is still TBD. It could be in a new package or part web_components. We prefer to not put it in the initialize package itself, to keep dependencies simple and keep initilize independent of HTML features.

Initialization API

To initialize the web application, we replace the static initialization method, instead of using init.dart, we use html_init.dart:

import 'package:html_init/html_init.dart' as htmlInit;
main() => htmlInit.run();

Execution semantics

We'll ensure that initialization is executed in post-order of HTML imports and Dart script tags. Within an HTML file, HTML imports and Dart script tags are visited in the order they appear in the HTML document.

As discussed in the static initializers design doc, we are guaranteed that init.run executes each initializer only once, even if we invoke it multiple times. In addition, the new Dart execution semantics ensures that if two script tags share a common package: library, then it is canonicalized to be the same library. These two features make it possible to build htmlInit.run as a second tier on top of the static initialization package, since we can guarantee that a library will be initialized once, even if two separate Dart script tags share an import to such library.

The html_init implementation, however, still needs to pay attention to canonicalize libraries imported via packages/ vs package: imports. This is important because HTML imports cannot use package: imports, but our convention is that they are treated the same.

Support for Dart-to-HTML imports

In a separate proposal we discuss in detail how to add support for importing HTML documents from Dart files. Internally, they are implemented as static initializers themselves.

For a given library containing Dart-to-HTML imports, our goal is to:

  • execute the initializers of the imported files before the initializers of the given library.
  • execute initializers in a deterministic order. In our case we picked to process Dart-to-HTML before Dart imports.

These features in the static initialization package make it possible to achieve those goals:

  • Initializer filters: We'll first run initializers for the actual Dart-to-HTML imports, and then separately run all other initializers in the code. This two-phase process guarantees that Dart-To-HTML imports are processed before any Dart imports. Without filters, we can only guarantee that at development time Dart-to-HTML imports are visited after Dart imports, but at deployment time the order is reversed.

  • Directives are processed first: Dart-to-HTML imports can be expressed as annotations on library directives, this helps make it easier to ensure that Dart-To-HTML imports are processed before any other initializers within the library.

  • Asynchronous initializers: Dart-to-HTML imports dynamically inject HTML imports, and hence code might be loaded asynchronously. This is not supported by Dartium today, but once it is, support for asynchronous initializers would allow us to wait until the code is loaded and run the initializers on that newly loaded code before we return from the import initializer itself. This helps preserve the post-order semantics.

Mirror-based implementation

To support development in Dartium, the html_init.run internally needs to:

  • crawl HTML imports and discover Dart script tags
  • for each Dart script tag (in post-order):
    • Canonicalize the script URL in order to treat URLs containing "packages/" as if they were written with the "package:" scheme.
    • Run the Dart-only initializer from it (init.run)

Turns out that the mirror loader library in polymer.dart has an implementation of most of the logic described here, except that it is tailored to support @CustomTag and @initMethod, instead of invoking a generic initialization library.

Transformer-based implementation

Except for a small piece, support for compilation for deployment is practically handled by transformers distributed in other parts of our system. In particular, there are 3 steps of transformation involved:

  1. combining script tags into a single program
  2. generating code to properly initialize the resulting program (provided by the static initialization package)
  3. inlining of HTML and Dart-to-HTML imports (provided by the web_components package)

Combining script tags (step 1) consists of basically:

  • create a new bootstrap Dart script that imports all other script tags and invokes their main. Imports must be added in the same order as they are discovered by visiting the HTML import graph in post-order.
  • remove all the original script tags, and in the location of the script tag in the main HTML page, inject a new script tag that loads the bootstrap Dart script created above.

This transformation is similar to what polymer.dart does today in script_compactor.dart.

After this step, all static initializers are in a single Dart script tag and the call to html_init.run() will simply initialize a single combined Dart program (that is, it is equivalent to just calling init.run()). The end result is that all initializers are invoked in the same deterministic order as they were invoked during development.

Finally, since we want to reduce the API surface to our users, we propose making all these three transformers be run as part of one group transformer in the web_components package. So a user can simply write a pubspec with this section:

transformers:
- web_components