| 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``. |