Helma Logo
main list history

Module System Proposal

from http://modulesjs.com/standard-proposal.html

discussion at http://groups.google.com/group/helma-ng/browse_thread/thread/6d002cb42a47ae42

The purpose of this document is to propose a contract between a collection of JavaScript module loaders and modules. The specification describes the environment that module loader implementations provide, and the environment that modules may depend upon. In particular, compliant module systems:

The specification is intended to be suitable for client- and server-side JavaScript module loader implementations.

The specification is intended to provide insights and an easy migration path to future versions of JavaScript.

The specification is intended to describe a JavaScript execution context that conformant JavaScript modules can depend upon such that they may be used with all conformant module loaders. This execution context could inform a "lint" implementation to verify conformance and permit additional variable renaming options for source code compressors like Packer, ShrinkSafe, and YUICompressor.


Module Execution Context

The singleton module object MUST be declared and memoized BEFORE the correpsonding module file is executed. The module file MUST only be executed ONCE for the duration of a page-view or JavaScript program.

In a module file's execution context, the context object, represented by this, MUST be the module object.

The scope chain, from global to local, of a module file's execution context MUST consist of:

global

Rules for module systems:

Rules for modules:

local

The local object is a module's private namespace for module loader functions and imported values.

The local object MUST provide:

The local object MAY provide the following functions for web-site implementations that need to support multi-module bundling. These functions would permit module constructor functions for more than one module file to be collected in a single file.

Modules MAY augment the local object with additional items.

Modules MUST NOT replace properties specified here.


Module Loading


require(<moduleUrl>, [<structure>])

The require function returns an object with properties copied from a foreign module. The URL refers to a file that could be executed to construct the module object. By default, all properties from the foreign module are copied into the returned object.

The returned object MAY be frozen.

Modules MUST NOT modify the returned object.

If a structure is provided, a subset of the items from the foreign module will be returned, the result of destructure(<module>, <structure>). If a function in the foreign module was declared with the foreignModuleBind decorator, the corresponding item in the returned object is the result of bind(<function>, <foreignModule>).

If the URL begins with a dot, ("."), the fully qualified URL for the requested module is resolved relative to the fully qualified URL of the current module. This would be the result of urlJoin(moduleRootUrl, moduleUrl, foreignModuleUrl). Otherwise the fully qualified URL is urlJoin(moduleRootUrl, foreignModuleUrl).

Regarding module file names:

  • Directory names and file names in modules MUST be in camelCase.
  • Modules MUST have a ".js" extension if they are provided by files.
  • Modules MUST not have an extension if they are provided by the module loader but are not backed by real files. This might include a "window" module in a particular browser implementation.
  • The module root is reserved for a cross-browser JavaScript standard library.
  • Modules provided by entities other than the standard library MUST exist in a subdirectory of the module root corresponding to a domain name controlled by the author, or a subdirectory thereof.

Module authors are encouraged to use module relative URLs to increase the mobility of entire directory trees of modules.


include(<moduleUrl>, [<structure>])

The include function defers to require, both its arguments and its return value. However, include also copies all of the items from the object returned by require to the moduleScope object.


foreignModuleBind(<function>)

A function decorator that denotes that the module loader will guarantees that, when the decorated function is called, it will receive the module object for the module file in which it was called as its context object: this. foreignModuleBind MAY return the same Function it was passed. The returned function MUST be usable in the module in which it was declared as if it were the function passed to foreingModuleBind. foreignModuleBind MAY modify properties of the given Function.

For example:

   this.foo = foreignModuleBind(function () {
       log("foo called from: " + this.local.moduleUrl);
   });

destructure(<object>, <structure>)

For the purpose of this specification, the "destructure" function has the following semantics. If the structure is an Array, the returned Object contains the properties from the given <object> corresponding to the keys provided in the given Array structure. If the structure is an Object, the returned object contains properties where each key corresponds to a value in the structure, and the value is a value from the object corresponding to the key in the structure. For example:

  • destructure({"a": 10, "b": 20}, ["a"]) == {"a": 10}
  • destructure({"a": 10, "b": 20}, {"a": "A"}) == {"A": 10}

module

The module object is a module's public interface. Adding items to the module object makes them available for export to other module local scopes. To that end, the module object is mutable. The module object MUST provide a local object. Modules MAY augment the module object.


moduleRootUrl

moduleRootUrl is the fully qualified URL of the JavaScript site-packages directory: the module root.


moduleUrl

moduleUrl is the relative URL of the current module file relative to moduleRootUrl.


print(<message>, [<label>])

print is a simple console logging function.

The module loader MAY ignore calls to print. The module loader MAY ignore the label argument.

The optional label MAY be any string, and MUST be suitable for use as a CSS class-name (preferably lower-case delimited by hyphens) of which the following MAY be significant:

  • info
  • warn
  • error
  • module
  • help
  • pass
  • fail

Afterword: Browser Implementations

This specification outlines the process of requiring modules from within other modules. However, in a browser's global execution context, JavaScript execution blocks are not modules. To that end, this specification does not require that the module loader be invoked in any particluar fashion. A particular implementation might hook an initial module to be loaded from within a script tag. Another implementation might scan the DOM for script tags with an alternate language attribute and execute them as modules with the current page's URL as their module URL. This specification only exists to maximize the portability of module files.


Afterword: ECMAScript import semantics.

A future version of the ECMAScript standard might specify new syntax and semantics for importing modules. Current discussions about this feature trend toward having new syntax that "desugars" to native JavaScript. To that end, I propose the following syntax and desugaring transformations in the context of this specification. The top line of each pair is akin to Python's import syntax and stands in for whatever syntax the next version of JavaScript divines. The second line is an equivalent, desugared version using the module system specified here.

  • import "<moduleUrl>" as <moduleName>;
  • module.<moduleName> = require("<moduleUrl>");
  • Comments

    #1 by Kris Kowal at 2008/09/04 20:42

    I would also like to propose renaming "log" to "print". Both names collide in browser implementations: console.log, Math.log, and window.print, but those can be called explicitly. "print" is more universally accepted, I think. I'm not a fan of "writeln", for the record.