umodule.js: first public release

This commit is contained in:
Loic Nageleisen 2013-08-19 18:48:06 +02:00
commit c375db3ddf
11 changed files with 306 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.js

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "uspec"]
path = uspec
url = https://github.com/lloeki/uspec-js.git

24
LICENSE Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2013, Loic Nageleisen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
Makefile Normal file
View file

@ -0,0 +1,22 @@
all: release
release: umodule.js
%.js: %.coffee
coffee --compile $<
uspec_module.js: uspec/uspec.js wrapper
./wrap_module uspec $< > $@
run_spec.js: umodule.js uspec_module.js umodule_spec.js
cat $^ > $@
phantom_spec: run_spec.js
phantomjs $<
spec: phantom_spec
clean:
@rm -f *.js
.PHONY: release node_spec phantom_spec spec

65
README.mdown Normal file
View file

@ -0,0 +1,65 @@
# µModule
µModule is a minimalist CommonJS module definition and requirement implementation, squarely
aimed at browsers.
## Usage
Load `umodule.js` before any other:
```html
<script src='/path/to/umodule.js'></script>
<script src='/path/to/modularized.js'></script>
...
```
Then in your application assets:
```coffee
# umodule.js is itsef a module and only exports `require` globally.
define = require('module').define
# define a module
define 'foo', (exports) ->
exports.hello = (w) -> 'hello, #{w}!'
```
Subsequently, in another asset:
```coffee
foo = require('foo')
console.log foo.hello()
```
There is no loader: all code is supposed to be loaded by an asset pipeline and
your typical user agent request. Therefore the goal is merely to isolate code
into modules and retrieve their exported interface in a given scope, using
CommonJS semantics. If you want a loader behavior, look into RequireJS.
## Examples
Although available as a global, require is passed as a second argument,
shadowing the global and (NIY) allowing for relative imports:
```coffee
Module = require('module')
Module.define 'bob/alice', (exports, require) ->
foo = require 'foo'
exports.eve = -> foo.hello()
```
The module itself is passed as a third argument.
```coffee
Module.define 'dave', (exports, require, module) ->
my_exports = require module.id
```
## Testing
Run with `make spec`. Testing depends on [uspec][uspec], included as a git submodule, and runs on phantomjs.
[uspec]: https://github.com/lloeki/uspec-js

39
umodule.coffee Normal file
View file

@ -0,0 +1,39 @@
###
# umodule.js v0.5
# (c) 2013 Loic Nageleisen
# Licensed under 3-clause BSD
###
root = global ? window
require = (id) ->
target = Module.root
target = target[item] for item in id.split('/')
throw new Error("module not found: #{id}") if typeof target is 'undefined'
target.exports
class Module
constructor: (@id) ->
@exports = {}
@define: (target, name, block) ->
if arguments.length < 3
[target, name, block] = [Module.root, arguments...]
top = target
target = target[item] or= new Module(item) for item in name.split '/'
block.call(target, target.exports, target.require, target)
target
require: -> require()
Module.root = new Module('root')
Module.root.exports = root
Module.root.module = new Module('module')
Module.root.module.exports = Module
root.require = require

70
umodule.js Normal file
View file

@ -0,0 +1,70 @@
// Generated by CoffeeScript 1.6.2
/*
# umodule.js v0.5
# (c) 2013 Loic Nageleisen
# Licensed under 3-clause BSD
*/
(function() {
var Module, require, root,
__slice = [].slice;
root = typeof global !== "undefined" && global !== null ? global : window;
require = function(id) {
var item, target, _i, _len, _ref;
target = Module.root;
_ref = id.split('/');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
item = _ref[_i];
target = target[item];
}
if (typeof target === 'undefined') {
throw new Error("module not found: " + id);
}
return target.exports;
};
Module = (function() {
function Module(id) {
this.id = id;
this.exports = {};
}
Module.define = function(target, name, block) {
var item, top, _i, _len, _ref, _ref1;
if (arguments.length < 3) {
_ref = [Module.root].concat(__slice.call(arguments)), target = _ref[0], name = _ref[1], block = _ref[2];
}
top = target;
_ref1 = name.split('/');
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
item = _ref1[_i];
target = target[item] || (target[item] = new Module(item));
}
block.call(target, target.exports, target.require, target);
return target;
};
Module.prototype.require = function() {
return require();
};
return Module;
})();
Module.root = new Module('root');
Module.root.exports = root;
Module.root.module = new Module('module');
Module.root.module.exports = Module;
root.require = require;
}).call(this);

70
umodule_spec.coffee Normal file
View file

@ -0,0 +1,70 @@
uspec = require('uspec')
describe = uspec.describe
pending = uspec.pending
assert = uspec.assert
assert_throws = uspec.assert_throws
Module = require('module')
describe 'require',
# CommonJS
'should be a function': ->
assert -> require instanceof Function
'should accept a module identifier': -> pending('how to test that?')
'should return the exported API of the foreign module': ->
Module.define 'foo', (exports) -> exports.bar = 'bar'
assert -> require('foo').bar isnt undefined
assert -> require('foo').bar == 'bar'
'should throw an error if the module cannot be returned': ->
assert_throws Error, -> require 'nonexistent/module'
describe 'Module',
# CommonJS
'should have an `export` var, that is an Object': ->
Module.define 'foo', (exports) ->
assert -> typeof exports == 'object'
'should have a `require` var, conformant with the spec': ->
Module.define 'foo', (exports, require) ->
assert -> typeof require == 'function'
'should have a `module` var': ->
Module.define 'foo', (exports, require, module) ->
assert -> module instanceof Module
assert -> module.id == 'foo'
'should export by adding to the exports var': ->
m = Module.define 'foo', (exports) -> exports.bar = 'bar'
assert -> m.exports.bar == 'bar'
'should have id property': ->
m = Module.define 'foo', ->
assert -> m.id isnt undefined
'should have its id set so that requiring it returns its exports': ->
m = Module.define 'foo', (exports) -> exports.bar = 'bar'
assert -> require(m.id) == m.exports
# Other
'should define a Module class': ->
assert -> Module isnt undefined
'should define a root module': ->
assert -> Module.root isnt undefined
assert -> Module.root.id == 'root'
results = uspec.run()
rc = if uspec.summary(results) then 0 else 1
phantom.exit(rc) unless typeof phantom is 'undefined'
process.exit(rc) unless typeof process is 'undefined'

1
uspec Submodule

@ -0,0 +1 @@
Subproject commit ec516c106c35d2a66b124a2a5497e17d5377575b

5
wrap_module Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
sed -e "s/#MODULE_NAME#/$1/" -e "/#MODULE#/ {
r $2
d
}" wrapper

6
wrapper Normal file
View file

@ -0,0 +1,6 @@
(function () {
Module = require('module');
Module.define('#MODULE_NAME#', function (exports, require, module) {
#MODULE#
});
}).call(this);