Helios Kernel — include() for JavaScript |
helios-kernel-0.9.7.tar.gz
13 kb
https://github.com/asvd/helios-kernel
Helios Kernel is an isomorphic JavaScript module loader and dependency manager. Under isomorphic it is implied that an application or a library based upon the Helios Kernel can run both in browser-based environment and in Node.js natively and without any kind of conversion. For the moment of writing, this is the only module loader focused on being truly isomorphic.
Helios Kernel tracks the dependency graph, loads and unloads corresponding modules dynamically in the runtime according to the needs of different and independent parts of an application. It is smart enough to start initializing the modules which are ready for that, while others are still being downloaded or parsed, and to handle some tricky problems such as circular dependencies or broken code (reporting the problem, but still keeping the application alive). But the key feature of Helios Kernel is
Helios Kernel provides the necessary features intended to make dependency management simple and straightforward. Syntax of a module and dependency declaration is implemented in the classic include-style:
// list of dependencies include('path/to/someLibrary.js'); include('../path/to/anotherLibrary.js'); init = function() { // module code, // objects declared in the included modules are available at this point LIB.someLibrary.sayHello(); }
A set of dependencies is listed in the module head using the include() function, where each call stands for a single dependency. The only argument is the exact path to the source of the needed module — so that it is always easy to find out the particular dependency source location.
Module code is located inisde the init() function declaration. It will be issued by Helios Kernel as soon as all dependencies are loaded.
This is how someLibrary.js included in the module above could look:
init = function() { // object containing the library routines LIB.someLibrary = {}; // library method LIB.someLibrary.sayHello = function() { console.log('Hello World!'); } }
Helios Kernel introduces a special global object called LIB, which serves as a registry designed to store the libraries. In this example a library object someLibrary is declared as a property of the LIB object. Other modules may then access the library routines over the LIB registry.
This differs to a more common approach of objects exporting, which is based on the module pattern, aims to provide the precise control over the objects created by a library, and thus considered as a must-have nowadays and reused in most of other loaders. However the disadvantage of such technique is the overheads during managing the dependency structure, introduced by an artificial coupling between a module (library internal structure) and the exported object (library interface).
Using Helios Kernel one may split a library into modules with the most logical and convenient way without being linked to the objects created by the particular modules. Moreover Helios Kernel does not apply any restrictions on how to transfer the data between the modules or what to perform at all inside a module body.
By convention a library stores its routines as a property of the LIB registry, and the name of that property should match the library name. A set of modules which the library is split into may build-up and fill-in the library object.
This is basicly everything you need to know to start using Helios Kernel for setting-up the dependencies in your project. This text contains the full documentation on Helios Kernel.
There is no native dependency management solution in browser environments. To ensure that a set of JavaScript-libraries is loaded, the libraries are usually listed within a single html-page header. Managing the dependencies and loading order is too tricky in this case, and gets more and more complicated along with the project growth. To solve this problem, several script loading approaches and libraries exist, one of which is Helios Kernel.
In Node.js there is a native dependency declaration technique — the require() function which implements the specifications suggested by CommonJS group. Just like as in most of the browser-based solutions, it introduces object exporting, which as mentioned more likely brings unnecessary complications to dependency management and objects declaration. Therefore Helios Kernel pretends to be a simplier and more flexible alternative.
Helios Kernel may be preferred because of its simplicity, or if there is a need to to have the modules isomorphic and compatible between web and Node.js environments. Comparing to other dependency-management solutions, with Helios Kernel it could be more convenient to:
$ npm install helios-kernel
// include the needed libraries here init = function() { // start your application code here console.log('Hello world!'); }To declare the initial module dependencies, use include() function at the module head
<script src="helios-kernel/kernel.js"></script> <script> window.onload = function(){ kernel.require('main.js'); } </script>
require('helios-kernel/kernel.js'); kernel.require(__dirname + '/main.js');If you have used npm to install Helios Kernel, you may also do it like this:
require('helios-kernel'); kernel.require(__dirname + '/main.js');
$ node nodestart.js
$ npm install helios-kernel
Optionally you can even convert and merge a Kernel-compatible library using the helios-merge tool into a plain JavaScript bundle suitable for using without Helios Kernel.
Module structure is explained in the first section of this text. Basically a module consists of the two parts: the list of dependencies in the module head using the include() function, and the module code inside the init() function declaration:
// list of dependencies include('path/to/someLibrary.js'); include('../path/to/anotherLibrary.js'); init = function() { // module code, // objects declared in the included modules are available at this point LIB.someLibrary.sayHello(); }
The simpliest approach to make the module data available to other modules, is to put the data inside a library object in the LIB registry.
To load a module in the runtime, use kernel.require() function. It takes three arguments — absolute path of the module, and the two callbacks — for a success and for a failure.
Unlike include() which is used for declaring a dependency in a module head, and is mostly intended to work with relative paths, kernel.require() only accepts specifying the absolute path. For a web environment you may start it with the slash / which will stand for the domain root. To load a remote module, provide the full URL starting with a protocol (http://...). You may also provide an array of paths to load several modules at once.
Second argument, the success callback, is a function which is called after all demanded modules and their dependencies are successfully loaded and initialized. Inside this callback you may start using the objects provided by the requested modules.
Third argument is a failure callback which will be called in case if some of the requested modules (or their dependencies) has failed to be loaded. Reasons could be very different (from syntax error to network problems), therefore you must implement some reasonable fallback or cancellation behaviour for the loading failure.
The returned value of kernel.require() is a reservation ticket, a special object corresponding to a single require() act. You will need the ticket if you wish to unload the requested modules in the future when you don't need them anymore.
Therefore, dynamically loading a module looks like this:
var sCallback = function() { // the library is loaded, we may use it now LIB.someLibrary.doSomething(); } var fCallback = function() { // library failed to load, falling back console.log('Cannot go any further!'); } var ticket = kernel.require( '/path/to/library.js', sCallback, fCallback );
After you have finished using the requested modules, you may release the ticket by calling kernel.release() function:
kernel.release(ticket);
This will not make the Kernel to unload the related modules immediately, instead the Kernel will know that they are not needed anymore at the area related to the given ticket, and will unload the modules after they will be released everywhere else.
Upon the module unload, its code is removed from the Kernel module cache. But Kernel does not track the objects created by the module's initializer, therefore you may provide an uninitializer function which would remove the library objects. This function will be called during the module unload.
Therefore the full version of a library module could look like this:
// module initializer init = function() { // object containing the library routines LIB.someLibrary = {}; // library method LIB.someLibrary.sayHello = function() { console.log('Hello World!'); } } // module uninitializer uninit = function() { // removing objects created in the initializer LIB.someLibrary = null; delete LIB.someLibrary; }
If a module initializer declares a single compound object encapsulating all the library routines (the recommended way, someLibrary in the example above), simply clearing that object should be enough, garbage collector should (hopefully) do the rest.
If you have a library of any format, it usually defines a set of routines which should be used from the outside later. In most of the web-libraries which are intended to be included using the <script> tag, a set of global objects is defined. In this case it should be enough to wrap the library code with the init() function declaration. So if the original source was like this:
libraryObject = { ... }; libraryFunction = function() { ... };
then it should be remade like this:
init = function() { libraryObject = { ... }; libraryFunction = function() { ... } }
Now this is a Helios Kernel compatible module, and could be loaded using the include() function. If you load this module, the code of library's init() function will be issued and initialize the objects before the init() function code of the module which requested the library.
For the libraries using some kind of export object, the whole code should also be wrapped with the init() function. To make the exported objects, they could be globally redeclared. For instance, a CommonJS module could looks like this:
this.someObject = { ... }; this.someFunction = function() { ... };
To be converted to the Helios module, it should be remade like this:
init = function() { // global library object someLibrary = {}; someLibrary.someObject = { ... }; someLibrary.someFunction = function() { ... } }
After this module is loaded, its routines could be referred to as someLibrary.someObject.
|
Helios Kernel library is licensed under the MIT license,
see http://github.com/asvd/helios-kernel
|