mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
149 lines
5.3 KiB
Markdown
149 lines
5.3 KiB
Markdown
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;
|
|
}
|
|
```
|