This document describes the Sky module system.
The Sky module system is based on the import element. In its most basic form, you import a module as follows:
<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 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 document waits until its list of outstanding dependencies is empty before the module it represents is marked complete.
Each module consists of one or more libraries. The first library in a module is the element tree library, which imports the sky:core module and then consists of the following code for a Sky module:
final Module module = new Module();
...and the following code for a Sky application:
final Module module = new Application();
The <script> elements found in the document create the subsequent libraries. Each one first imports the dart:mirror library, then the sky:core 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.
At the end of the <script> block's source, if it parsed correctly and completely, the following code is appended:
class _ { } module.registerElements(reflectClass(_).owner);
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 sky:core as follows:
TODO(ianh): dartification of the rest of this file
When importing a module into another, Sky runs the following steps:
exports value“Try to import” a value means to run the following steps:
registerElement()), call this importer module's registerElement() with the valuedictionary 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) } class Module : AbstractModule { constructor (Application application, Document document, String url); // O(1) readonly attribute Application application; // O(1) } class Application : AbstractModule { constructor (Document document, GestureManager gestureManager, String url); // O(1) attribute String title; // O(1) readonly attribute GestureManager gestureManager; }
The as attribute on the import element binds a name to the imported module:
<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:
(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.