blob: 13c8ebb26b61783efd124df460d2f96a6def0555 [file] [log] [blame] [view] [edit]
Sky Module System
=================
This document describes the Sky module system.
Overview
--------
The Sky module system is based on the ``import`` element. In its
most basic form, you import a module as follows:
```html
<import src="path/to/module.sky" />
```
As these ``import`` elements are inserted into a module's element
tree, the module's list of outstanding dependencies grows. When an
imported module completes, it is removed from the importing module's
list of outstanding dependencies.
Before compiling script or inserting an element that is not already
registered, the parser waits until the list of outstanding
dependencies is empty. After the parser has finished parsing, the
module waits until its list of outstanding dependencies is empty
before marking itself complete.
The ``as`` attribute on the ``import`` element binds a name to the
imported module:
```html
<import src="path/to/chocolate.sky" as="chocolate" />
```
Each module implicitly imports the [Built-In Elements
Module](builtins.md).
When a module imports another, and the ``import`` element has no
``as`` attribute, then any elements registered in that module whose
tag names do not begin with an underscore must be registered on the
importing module. (If multiple elements are registered with the same
name, that name gets marked as dead for that module and all the
registrations for that name are discarded.)
TODO(ianh): decide if elements imported with "as" should be imported
but with the "as" name prefixed, as in ``<foo.button>``
Module API
----------
Each module consists of one or more libraries. The first library in a
module is the *element tree library*, which consists of the following
code for a Sky module:
```dart
import 'dart:sky';
final Module module = new Module();
```
...and the following code for a Sky application:
```dart
import 'dart:sky';
final Module module = new Application();
```
The ``<script>`` elements found in the module's element tree create
the subsequent libraries. Each one first imports the ``dart:mirror``
library, then the ``dart:sky`` module, then the first library
described above, then all the modules referenced by ``<import>``
element up to that ``<script>`` element and all the libraries defined
by ``<script>`` elements up to that point, interleaved so as to
maintain the same relative order as those elements were first seen by
the parser.
When a library imports a module, it actually imports all the libraries
that were declared by that module except the aforementioned element
tree library. If the ``as`` attribute is present on the ``import``
element, all the libraries are bound to the same name.
At the end of the ``<script>`` block's source, if it parsed correctly
and completely, the conceptual equivalent of the following code is
appended (but without affecting the library's list of declarations and
without any possibility of it clashing with identifiers described in
the library itself):
```dart
class _ { }
void main(ScriptElement script) {
LibraryMirror library = reflectClass(_).owner as LibraryMirror;
if (library.declarations.containsKey(#_init) && library.declarations[#_init] is MethodMirror)
_init(script);
AutomaticMetadata.runLibrary(library, module, script);
}
```
Then, that ``main(script)`` function is called, with ``script`` set to
the ``ScriptElement`` object representing the relevant ``<script>``
element.
TODO(ianh): decide what URL and name we should give the libraries, as
exposed in MirrorSystem.getName(libraryMirror.qualifiedName) etc
The ``Module`` class is defined in ``dart:sky`` as follows:
```dart
abstract class AbstractModule extends EventTarget {
AbstractModule({this.url, this.elements});
final String url;
final Root elements; // O(1)
// the Root node of the module or application's element tree
external Future<Module> import(String url); // O(Yikes)
// load and return the URL at the given Module
// if it's already loaded, the future will resolve immediately
// if loading fails, the future will have an error
external List<Module> getImports(); // O(N)
// returns the Module objects of all the imported modules
external void registerElement(String tagname, Type elementClass); // O(1)
// registers a tag name with the parser
// only useful during parse time
// verify that tagname isn't null or empty
// verify that elementClass is the Type of a class that extends Element (directly or indirectly, but not via "implements" or "with")
// (see the @tagname code for an example of how to verify that from dart)
// verify that there's not already a class registered for this tag name
// if there is, then mark this tagname is broken, so that it acts as if it's not registered in the parser,
// and, if this is the first time it was marked broken, log a console message regarding the issue
// (mention the tag name but not the classes, so that it's not observable that this currently happens out of order)
}
class Module extends AbstractModule {
Module({String url, Root elements, this.application}) :
super(url: url, elements: elements); // O(1)
final Application application; // O(1)
}
class Application extends AbstractModule {
Application({String url, Root elements, this.gestureManager}) :
super(url: url, elements: elements); // O(1)
external String get title; // O(1)
external void set title(String newValue); // O(1)
final GestureManager gestureManager;
}
```