mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
166 lines
6.1 KiB
Markdown
166 lines
6.1 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 document, the
|
|
document's list of outstanding dependencies grows. When an imported
|
|
module completes, it is removed from the document's list of
|
|
outstanding dependencies.
|
|
|
|
Before executing 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
|
|
document waits until its list of outstanding dependencies is empty
|
|
before the module it represents is marked complete.
|
|
|
|
|
|
Module API
|
|
----------
|
|
|
|
Within a script in a module, the ``module`` identifier is bound to
|
|
the ``Module`` object that represents the module.
|
|
|
|
### Exporting values ###
|
|
|
|
A module can export a value by assigning the ``exports`` property of
|
|
its ``Module`` object. By default, the ``exports`` property of a
|
|
``Module`` is an empty Object. Properties can be added to the object,
|
|
or, it can be set to an entirely different object; for example, it
|
|
could be set to the module's ``Document`` itself, in case the point of
|
|
the module is to expose some ``template`` elements.
|
|
|
|
### Exporting element definitions ###
|
|
|
|
When importing a module into another, Sky runs the following steps:
|
|
- let export be the imported module's ``exports`` value
|
|
- try to import export
|
|
- if that fails:
|
|
- try to import each property of export
|
|
|
|
"Try to import" a value means to run the following steps:
|
|
- if the value is an element constructor (generated by
|
|
``registerElement()``), call this importer module's
|
|
``registerElement()`` with the value
|
|
|
|
### IDL ###
|
|
|
|
```javascript
|
|
dictionary InternalElementOptions {
|
|
String tagName;
|
|
Boolean shadow = false;
|
|
Object prototype = Element;
|
|
}
|
|
interface InternalElementConstructorWithoutShadow {
|
|
constructor (Module hostModule);
|
|
attribute String tagName;
|
|
}
|
|
interface InternalElementConstructorWithShadow {
|
|
constructor (Module hostModule);
|
|
attribute String tagName;
|
|
attribute Boolean shadow;
|
|
}
|
|
typedef ElementRegistrationOptions (InternalElementOptions or
|
|
InternalElementConstructorWithoutShadow or
|
|
InternalElementConstructorWithShadow);
|
|
|
|
abstract class AbstractModule : EventTarget {
|
|
readonly attribute Document document; // O(1) // the Document of the module or application
|
|
Promise<any> import(String url); // O(Yikes) // returns the module's exports
|
|
private Array<Module> getImports(); O(N) // returns the Module objects of all the imported modules
|
|
|
|
readonly attribute String url;
|
|
|
|
ElementConstructor registerElement(Object options); // O(1)
|
|
// if you call registerElement() with an object that was created by
|
|
// registerElement(), it just returns the object after registering it,
|
|
// rather than creating a new constructor
|
|
// otherwise, it proceeds as follows:
|
|
// - if options is a Function (i.e. it is either an
|
|
// InternalElementConstructorWithoutShadow object or an
|
|
// InternalElementConstructorWithShadow object), then let
|
|
// constructor be that function, and let prototype be that
|
|
// functions's prototype; otherwise, let constructor be a no-op
|
|
// function and let prototype be the prototype property of the
|
|
// object passed in (the InternalElementOptions; prototype
|
|
// defaults to Element).
|
|
// - let shadow be option's shadow property's value coerced to a
|
|
// boolean, if the property is present, or else the value false.
|
|
// - let tagName be option's tagName property's value.
|
|
// - create a new Function that acts as if it had the signature of
|
|
// the constructors in the ElementConstructor interface, and that
|
|
// runs the follows steps when called:
|
|
// - throw if not called as a constructor
|
|
// - create an actual element object (the C++-backed object)
|
|
// called tagName, along with the specified attributes
|
|
// - initialise the shadow tree if shadow is true
|
|
// - call constructor, if it's not null, with the module
|
|
// within which the new element is being constructed as the
|
|
// argument
|
|
// - append all the specified children
|
|
// - mark that new Function as created by registerElement() so that
|
|
// it can be recognised if used as an argument to
|
|
// registerElement()
|
|
// - let that new Function's prototype be the aforementioned prototype
|
|
// - let that new Function have tagName and shadow properties set to
|
|
// the aforementioned tagName and shadow
|
|
// - register the new tagName with this constructor
|
|
// - return the new Function (which is, not coincidentally, an
|
|
// InternalElementConstructorWithShadow)
|
|
|
|
readonly attribute ScriptElement? currentScript; // O(1) // returns the <script> element currently being executed if any, and if it's in this module; else null
|
|
}
|
|
|
|
class Module : AbstractModule {
|
|
constructor (Application application, Document document, String url); // O(1)
|
|
readonly attribute Application application; // O(1)
|
|
|
|
attribute any exports; // O(1) // defaults to {}
|
|
}
|
|
|
|
class Application : AbstractModule {
|
|
constructor (Document document, String url); // O(1)
|
|
attribute String title; // O(1)
|
|
}
|
|
```
|
|
|
|
|
|
Naming modules
|
|
--------------
|
|
|
|
The ``as`` attribute on the ``import`` element binds a name to the
|
|
imported module:
|
|
|
|
```html
|
|
<import src="path/to/chocolate.sky" as="chocolate" />
|
|
```
|
|
|
|
The parser executes the contents of script elements inside a module as
|
|
if they were executed as follow:
|
|
|
|
```javascript
|
|
(new Function(name_1, ..., name_n, module, source_code)).call(
|
|
value_1, ..., value_n, source_module);
|
|
```
|
|
|
|
Where ``name_1`` through ``name_n`` are the names bound to the
|
|
various named imports in the script element's document,
|
|
``source_code`` is the text content of the script element,
|
|
``source_module`` is the ``Module`` object of the script element's
|
|
module, and ``value_1`` through ``value_n`` are the values
|
|
exported by the various named imports in the script element's
|
|
document.
|
|
|
|
When an import fails to load, the ``as`` name for the import gets
|
|
bound to ``undefined``.
|