-
Notifications
You must be signed in to change notification settings - Fork 33
Dart to HTML Imports
This document presents a design for an annotation that allows users to write HTML imports from Dart code. This proposal relies on an existing proposal to add an initialization package.
We'd like to integrate HTML Imports and Dart imports, so people can use custom elements, and any HTML files those elements depend on will be transitively imported. This will allow developers of custom elements to encapsulate the use of HTML imports in Dart and will reduce the duplication there is between HTML imports and Dart imports today.
The main motivating example today is our implementation of the material design for Dart. The current implementation is written with custom elements and we provide Dart proxies with a Dart friendly API to use them. Importing a proxy is not enough to use the element, users so far need to replicate the import in HTML. Dart-to-HTML imports will let us remove the duplicate import in HTML, instead the proxies will declare what HTML needs to be included so that the elements work correctly.
We propose adding an @HtmlImport
annotation that indicates that Dart is
interested in loading HTML code. During development time, the annotation behaves
as a static initializer that dynamically injects the HTML import to the page.
For deployment, a transformer will be used to eliminate the dynamic injection of
HTML imports and replace them with actual inlined HTML code.
An Dart-to-HTML import is written as an annotation attached to a library element:
@HtmlImport('paper_dialog.html')
library paper_elements.paper_dialog;
technically the annotation could go somewhere else, but because our implementation builds on top of initializers, using a directive makes it clear that we intend to resolve and initialize Dart-to-HTML imports before any other initializers in library are executed.
The basic idea behind an @HtmlImport
in a Dart library is that it should be
equivalent to have written an actual <link rel="import">
in the HTML document
that transitively loaded such library.
@HtmlImport
takes a string URI argument, which is resolved with respect to the
URI of the file where it appears. The resolved URI will be used for the actual
HTML import. This argument is allowed to use package:
schemes, and they will
be canonicalized first to ensure that they are equivalent to HTML imports
written as relative paths reaching into the packages/
directory.
Initially this design doesn't support having Dart script tags in the imported
HTML page. This is due to limitations on Dartium today that don't allow loading
extra Dart script tags after the isolate starts executing (more details below).
If that limitation were to be removed, we could remove our restriction. In that
case, if the HTML import includes additional Dart script tags, their
initializers would be executed before other initializers on the library
containing the @HtmlImport
annotation and before initializers of Dart
libraries imported from that library directly in Dart.
This design allows users to export custom elements so that they can be imported
via Dart or via traditional HTML imports, so if someone wants to consume an
element, she can choose the best way to import it for her app. Note that, even
if Dart script tags are not allowed in HTML files included via @HtmlImport
s,
it is still possible to do this by following a pattern of indirection with 2
HTML files:
- my_element.dart:
// Users can consume <my-element> by importing this from Dart.
@HtmlImport('my_element_nodart.html')
library my_element;
...
- my_element.html:
<!-- Users can consume <my-element> in HTML by importing
this file from their HTML files. -->
<link rel="import" href="my_element_nodart.html>
<script type="application/dart" src="my_element.dart"></script>
- my_element_nodart.html:
<!-- This file contains the actual definition of the element,
but it doesn't include any Dart script tags -->
<polymer-element name="my-element">...</polymer-element>
Dart-to-HTML will have two implementations. One that works in Dartium without any code transformations, and another that uses transformations that can then be compiled with dart2js.
To fully comply with our semantics, we need to do two passes of static
initialization: one pass to only run through each @HtmlImport
and inline HTML
documents, and a second pass that performs all other initializations. This will
ensure that initialization is done in the proper order.
The transformer-based implementation simply consists on adding HTML imports to HTML files. Other transformations, such as inlining or static initialization, will be handled separately by other parts of the system, so we don't need to worry about them here.
The transformation algorithm is as follows:
- Find all Dart script tags in a given HTML file
- Traverse the import graph on each Dart script tag and find any
@HtmlImport
annotation. - Resolve the URI for the annotation, and add the corresponding
<link rel="import">
on the HTML file - repeat for each HTML file in the project.