├── .gitignore
├── package.json
├── README.md
├── transport.js
├── tests
├── sandbox
│ ├── evaluate.js
│ ├── require.js
│ ├── creation.js
│ └── misc.js
├── packages-test.js
├── require-test.js
├── loader-test.js
├── relative-require-test.js
├── normalize-test.js
└── async-test.js
├── LICENSE.txt
└── lib
└── spade.js
/.gitignore:
--------------------------------------------------------------------------------
1 | Gemfile.lock
2 | .bundle
3 | bin
4 | *.bpkg
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spade",
3 | "version": "1.0.2",
4 |
5 | "description": "Dependency Management Library for JavaScript",
6 | "summary": "Dependency Management Library for JavaScript",
7 | "author": "Charles Jolley",
8 | "homepage": "http://github.com/sproutcore/spade",
9 |
10 | "directories": {
11 | "lib": "lib",
12 | "tests": "tests"
13 | },
14 |
15 | "dependencies:development": {
16 | "qunit": "~> 1.0.0"
17 | },
18 |
19 | "bpm:provides": {
20 | "transport": {
21 | "main": "spade/transport"
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | spade - JavaScript module loader
2 |
3 | Spade is a module loader for use with [bpm](https://github.com/getbpm/bpm)
4 | By default BPM loads all JavaScript files alphabetically. However, for
5 | more complex projects this is undesirable. Spade adds a require method
6 | that can be called to load and execute specific files.
7 |
8 | ## Basic Usage
9 |
10 | In an existing BPM project:
11 |
12 | 1. Add spade as a dependency
13 |
14 | bpm add spade
15 |
16 | 2. Update your index.html
17 | When using spade, files are no longer automatically loaded, so you'll
18 | have to update your index.html to load your main.js. After bpm_lib.js
19 | is sourced add the following line:
20 |
21 |
22 |
23 | 3. Start using `require` in your JavaScript!
24 |
25 | ## Useful Tips
26 |
27 | Like node.js, require can return a JavaScript object. However, unlike
28 | node.js you can define globals within your files. The ability to define
29 | globals makes it easier to develop in a browser environment.
30 |
--------------------------------------------------------------------------------
/transport.js:
--------------------------------------------------------------------------------
1 | /*globals BPM_PLUGIN */
2 |
3 | BPM_PLUGIN.compileTransport = function(code, context, filename) {
4 | var format = context.settings['spade:format'];
5 | if (format === 'none') { return code };
6 |
7 | var ret = '',
8 | packageName = context['package'].name,
9 | id = packageName+'/'+context.moduleId;
10 |
11 | // Register package, probably a better way to do this
12 | if (id.match(/^[^\/]+\/main$/)) {
13 |
14 | var ctx = context['package'],
15 | pkg = { name: ctx.name,
16 | version: ctx.version,
17 | dependencies: ctx.dependencies };
18 |
19 | ret += 'spade.register("'+packageName+'", '+JSON.stringify(pkg)+');\n\n';
20 | }
21 |
22 | if (format === 'function') {
23 | code = 'function(require, exports, __module, ARGV, ENV, __filename){\n'+code+'\n}';
24 | } else {
25 | code = context.minify("(function(require, exports, __module, ARGV, ENV, __filename){"+code+"\n});");
26 | code = JSON.stringify(code);
27 | }
28 |
29 | ret += 'spade.register("'+id+'", '+code+');';
30 |
31 | return ret;
32 | };
33 |
34 |
--------------------------------------------------------------------------------
/tests/sandbox/evaluate.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2011 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade, Sandbox = spade.Sandbox;
11 |
12 |
13 | module('spade: Sandbox evaluation', {
14 | setup: function() {
15 | t.sandbox = new Sandbox(new Spade());
16 | },
17 |
18 | teardown: function() {
19 | delete t.sandbox;
20 | }
21 | });
22 |
23 | test('normal', function(){
24 | equal(t.sandbox._evaluatorInited, undefined);
25 | equal(t.sandbox.evaluate('2 * 2'), 4);
26 | equal(t.sandbox._evaluatorInited, true);
27 | });
28 |
29 | test('already initialized', function(){
30 | // Initialize
31 | t.sandbox.evaluate('');
32 | // Test
33 | equal(t.sandbox.evaluate('3 * 3'), 9);
34 | });
35 |
36 | test('destroyed', function(){
37 | t.sandbox.destroy();
38 | raises(function(){ t.sandbox.evaluate('4 * 4'); }, Error, "Sandbox destroyed");
39 | });
40 |
41 | })();
42 |
43 |
--------------------------------------------------------------------------------
/tests/sandbox/require.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2011 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade, Sandbox = spade.Sandbox;
11 |
12 |
13 | module('spade: Sandbox require', {
14 | setup: function() {
15 | t.sandbox = new Sandbox(new Spade());
16 | t.sandbox.spade.register('testing', { name: 'testing' });
17 | t.sandbox.spade.register('testing/main', "exports.hello = 'hi';");
18 | },
19 |
20 | teardown: function() {
21 | delete t.sandbox;
22 | }
23 | });
24 |
25 | test("require new", function(){
26 | equal(t.sandbox.require('testing').hello, 'hi');
27 | });
28 |
29 | // NOTE: This test doesn't necessarily tell us that anything special is happening, just that it works
30 | test("require existing", function(){
31 | // Cache it
32 | t.sandbox.require('testing');
33 | // Now require again
34 | equal(t.sandbox.require('testing').hello, 'hi');
35 | });
36 |
37 | test("throw if doesn't exist", function(){
38 | raises(function(){ t.sandbox.require('missing'); }, "Module missing not found");
39 | });
40 |
41 | })();
42 |
43 |
44 |
--------------------------------------------------------------------------------
/tests/sandbox/creation.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2011 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade, Sandbox = spade.Sandbox;
11 |
12 |
13 | module('spade: Sandbox Creation', {
14 | setup: function() {
15 | t.spade = new Spade();
16 | },
17 |
18 | teardown: function() {
19 | delete t.spade;
20 | }
21 | });
22 |
23 | test('basic sandbox', function() {
24 | var spade = t.spade,
25 | sandbox = new Sandbox(spade);
26 |
27 | // note: using equal() here causes an infinite loop for some reason
28 | ok(sandbox.spade === spade, 'sandbox.spade == spade');
29 | equal(sandbox.name, '(anonymous)');
30 | equal(sandbox.isIsolated, false);
31 | });
32 |
33 | test('named sandbox', function() {
34 | var sandbox = new Sandbox(t.spade, 'Test Sandbox');
35 |
36 | equal(sandbox.name, 'Test Sandbox');
37 | });
38 |
39 | test('isolated sandbox', function() {
40 | var sandbox = new Sandbox(t.spade, 'Test Sandbox', true),
41 | sandbox2 = new Sandbox(t.spade, true);
42 |
43 | equal(sandbox.isIsolated, true);
44 | equal(sandbox2.isIsolated, true);
45 | });
46 |
47 | })();
48 |
49 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | ==========================================================================
2 | Spade 2.0 CommonJS Runtime
3 | copyright 2010 Strobe Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a
6 | copy of this software and associated documentation files (the "Software"),
7 | to deal in the Software without restriction, including without limitation
8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 | and/or sell copies of the Software, and to permit persons to whom the
10 | Software is furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 | DEALINGS IN THE SOFTWARE.
22 |
23 | Spade is part of the SproutCore project.
24 |
25 | SproutCore and the SproutCore logo are trademarks of Sprout Systems, Inc.
26 |
27 | For more information visit http://www.sproutcore.com/spade
28 |
29 | ==========================================================================
30 |
--------------------------------------------------------------------------------
/tests/packages-test.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2010 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade deepEqual */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade;
11 |
12 | module('spade: packages', {
13 | setup: function() {
14 | t.spade = new Spade();
15 | },
16 |
17 | teardown: function() {
18 | delete t.spade;
19 | }
20 | });
21 |
22 | test('should find registered package', function() {
23 |
24 | var spade = t.spade;
25 | spade.register('PKG', { name: 'PKG' });
26 |
27 | equal(spade.package('PKG').name, 'PKG');
28 | equal(spade.package('PKG/foo/bar').name, 'PKG');
29 |
30 | });
31 |
32 | test('should respect mappings', function() {
33 |
34 | var spade = t.spade;
35 | spade.register('PKG', { mappings: { foo: 'FOO' } });
36 |
37 | spade.register('PKG/bar', function(require, exports) {
38 | exports.id = require('foo/foo').id;
39 | });
40 |
41 | spade.register('FOO/foo', function(r, e) { e.id = 'FOO'; });
42 |
43 | equal(spade.require('PKG/bar').id, 'FOO'); // should remap pkg name
44 |
45 | });
46 |
47 | test('should set default directories', function() {
48 | var spade = t.spade;
49 | spade.register('PKG', { name: 'PKG' });
50 | deepEqual(spade.package('PKG').directories, { 'lib': ['lib'] });
51 | });
52 |
53 | })();
54 |
--------------------------------------------------------------------------------
/tests/require-test.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2010 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade raises */
7 |
8 | (function() {
9 |
10 | var t = {},
11 | Spade = spade.Spade;
12 |
13 | // ..........................................................
14 | // BASIC REQUIRE
15 | //
16 |
17 | module('spade: basic require', {
18 | setup: function() {
19 | t.spade = new Spade();
20 | },
21 |
22 | teardown: function() {
23 | delete t.spade;
24 | }
25 | });
26 |
27 | test('register then require a module', function() {
28 | var spade = t.spade;
29 |
30 | spade.register('foo/bar', function(require, exports) {
31 | exports.foo = 'bar';
32 | });
33 |
34 | var exp = spade.require('foo/bar');
35 | equal(exp.foo, 'bar', 'exports.foo == bar - means require succeeded');
36 | });
37 |
38 | test('register a string factory then require', function() {
39 | var spade = t.spade;
40 |
41 | spade.register('foo/bar', "exports.foo = 'bar';");
42 |
43 | var exp = spade.require('foo/bar');
44 | equal(exp.foo, 'bar', 'exports.foo == bar - means require succeeded');
45 | });
46 |
47 | test('require a non-existant module will throw an exception', function() {
48 | var spade = t.spade;
49 | raises(function() {
50 | spade.require('imaginary/foo');
51 | }, 'Module imaginary/foo not found');
52 | });
53 |
54 | test('require a module that was just registered symbolically. This is for compatibility with non-module items', function() {
55 | var spade = t.spade;
56 | spade.register('not/a-module');
57 | ok(spade.require('not/a-module'));
58 | });
59 |
60 | })();
61 |
--------------------------------------------------------------------------------
/tests/loader-test.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2010 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade;
11 |
12 | // ..........................................................
13 | // BASIC REQUIRE
14 | //
15 |
16 | module('spade: loader', {
17 | setup: function() {
18 | t.spade = new Spade();
19 |
20 | // preload a module
21 | t.spade.register('foo/main', function(r, e) { e.id = 'foo'; });
22 |
23 | // dummy loader loads only foo/bar on demand
24 | t.spade.loader = {
25 |
26 | requests: 0,
27 |
28 | loadFactory: function(spade, id, formats, done) {
29 | this.requests++;
30 | if (id === 'foo/bar') {
31 | spade.register(id, function(r,e) { e.id='foo/bar'; });
32 | }
33 | if (done) throw "should not be passed done"
34 | }
35 | };
36 | },
37 |
38 | teardown: function() {
39 | delete t.spade;
40 | }
41 | });
42 |
43 | test('should not talk to loader if module is registered', function() {
44 | var spade = t.spade;
45 | equal(spade.require('foo').id, 'foo', 'should find foo');
46 | equal(spade.loader.requests, 0, 'loader should not have been called');
47 | });
48 |
49 | test('should let loader register', function() {
50 | var spade = t.spade;
51 | equal(spade.require('foo/bar').id, 'foo/bar', 'should find foo');
52 | equal(spade.loader.requests, 1, 'loader should have been called');
53 | });
54 |
55 | test('should throw if loader does not register', function() {
56 | var spade = t.spade;
57 | raises(function() {
58 | spade.require('imaginary/bar');
59 | });
60 | equal(spade.loader.requests, 1, 'loader should have been called');
61 | });
62 |
63 | })();
64 |
--------------------------------------------------------------------------------
/tests/sandbox/misc.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2011 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade, Sandbox = spade.Sandbox;
11 |
12 |
13 | module('spade: Sandbox Miscellaneous', {
14 | setup: function() {
15 | t.sandbox = new Sandbox(new Spade(), 'Test Sandbox');
16 | },
17 |
18 | teardown: function() {
19 | delete t.sandbox;
20 | }
21 | });
22 |
23 | test('toString', function(){
24 | equal(t.sandbox.toString(), '[Sandbox Test Sandbox]');
25 | });
26 |
27 | test("exists", function(){
28 | t.sandbox.spade.register('test', { name: 'test' });
29 | t.sandbox.spade.register('test/main', '');
30 |
31 | ok(t.sandbox.exists('test'), "test should exist");
32 | ok(!t.sandbox.exists('missing'), "missing should not exist");
33 | });
34 |
35 | test("async", function(){
36 | t.sandbox.spade.register('test', { name: 'test' });
37 | t.sandbox.spade.register('test/main', 'exports.hello = "hi";');
38 |
39 | stop(1000);
40 | t.sandbox.async('test', function(err) {
41 | equals(err, null, 'should not return an error');
42 | start();
43 | });
44 | });
45 |
46 | test("url", function(){
47 | t.sandbox.spade.register('no-root', { name: 'no-root' });
48 | t.sandbox.spade.register('with-root', { name: 'with-root', root: 'root/url' });
49 |
50 | raises(function(){ t.sandbox.url('missing'); }, "Can't get url for non-existent package missing/main");
51 | raises(function(){ t.sandbox.url('no-root'); }, "Package for no-root/main does not support urls");
52 | equal(t.sandbox.url('with-root'), 'root/url/main');
53 | });
54 |
55 | test("destroy", function(){
56 | equal(t.sandbox.isDestroyed, false);
57 | t.sandbox.destroy();
58 | equal(t.sandbox.isDestroyed, true);
59 | });
60 |
61 | })();
62 |
63 |
--------------------------------------------------------------------------------
/tests/relative-require-test.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2010 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade;
11 |
12 | // ..........................................................
13 | // BASIC REQUIRE
14 | //
15 |
16 | module('spade: relative require', {
17 | setup: function() {
18 | t.spade = new Spade();
19 |
20 | ['foo', 'bar'].forEach(function(id) {
21 | t.spade.register(id, { "name": id });
22 | });
23 |
24 | // register some dummy modules. These will just set an 'id' prop on exports
25 | ['foo/bar', 'bar/main', 'foo/bar/baz'].forEach(function(id) {
26 | t.spade.register(id, function(r, e) { e.id = id; });
27 | });
28 | },
29 |
30 | teardown: function() {
31 | delete t.spade;
32 | }
33 | });
34 |
35 | test('require absolute', function() {
36 | var spade = t.spade;
37 |
38 | spade.register('blah/main', function(require, e) {
39 | e.found = require('foo/bar').id;
40 | });
41 |
42 | equal(spade.require('blah').found, 'foo/bar');
43 | });
44 |
45 | test('require relative top level', function() {
46 | var spade = t.spade;
47 | spade.register('blah/main', function(require, e) {
48 | e.found = require('../bar').id;
49 | });
50 |
51 | equal(spade.require('blah').found, 'bar/main');
52 | });
53 |
54 | test('require relative nested', function() {
55 | var spade = t.spade;
56 | spade.register('foo/blah', function(require, e) {
57 | e.found = require('./bar').id;
58 | });
59 |
60 | equal(spade.require('foo/blah').found, 'foo/bar');
61 | });
62 |
63 | test('require relative up nested', function() {
64 | var spade = t.spade;
65 | spade.register('bar/blah', function(require, e) {
66 | e.found = require('../foo/bar/baz').id;
67 | });
68 |
69 | equal(spade.require('bar/blah').found, 'foo/bar/baz');
70 | });
71 |
72 | })();
--------------------------------------------------------------------------------
/tests/normalize-test.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2010 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {};
11 | var t3;
12 |
13 | // ..........................................................
14 | // BASIC REQUIRE
15 | //
16 |
17 | module('spade: normalize', {
18 | setup: function() {
19 | t.spade = new spade.Spade();
20 | },
21 |
22 | teardown: function() {
23 | delete t.spade;
24 | }
25 | });
26 |
27 | test('normalize', function() {
28 | var spade = t.spade;
29 | equal(spade.normalize('foo/bar'), 'foo/bar');
30 | equal(spade.normalize('./foo', 'bar/baz'), 'bar/foo');
31 | equal(spade.normalize('../foo', 'bar/baz'), 'foo/main');
32 | equal(spade.normalize('foo/../bar//foo/./baz', 'bar/baz'), 'bar/foo/baz');
33 |
34 | equal(spade.normalize('/foo/./bar'), 'foo/bar');
35 | equal(spade.normalize('foo/../bar/'), 'bar/main');
36 | equal(spade.normalize('/foo/../bar/'), 'bar/main');
37 |
38 | equal(spade.normalize('/foo/bar'), 'foo/bar');
39 | equal(spade.normalize('foo/bar/'), 'foo/bar');
40 | equal(spade.normalize('/foo/bar/'), 'foo/bar');
41 |
42 | equal(spade.normalize('PKG/foo/bar'), 'PKG/foo/bar');
43 | equal(spade.normalize('BAR/foo', 'PKG/bar/baz'), 'BAR/foo');
44 | equal(spade.normalize('./foo', 'PKG/bar/baz'), 'PKG/bar/foo');
45 | equal(spade.normalize('../foo', 'PKG/bar/baz'), 'PKG/foo');
46 | equal(spade.normalize('./foo/../../bar//foo/./baz', 'PKG/bar/baz'), 'PKG/bar/foo/baz');
47 |
48 | });
49 |
50 | test('normalize package', function() {
51 | var spade = t.spade;
52 | spade.register('sproutcore', {}); // register as a package
53 | equal(spade.normalize('sproutcore'), 'sproutcore/main');
54 | equal(spade.normalize('foo/sproutcore'), 'foo/sproutcore');
55 | });
56 |
57 | test('normalize relative require from main', function() {
58 | // I think this is a valid test, but not certain
59 | var spade = t.spade, mainRequire, otherRequire;
60 | spade.register('foo', { main: './lib/foo', directories: { lib: './lib/foo' } });
61 | spade.register('foo/main', 'return require;');
62 | spade.register('foo/other/main', 'return require;');
63 | mainRequire = spade.require('foo/main');
64 | otherRequire = spade.require('foo/other/main');
65 | equal(mainRequire.normalize('./foo/adfadf'), 'foo/adfadf', 'works for real main');
66 | equal(otherRequire.normalize('./foo/core'), 'foo/other/foo/core', "no difference for fake main");
67 | });
68 |
69 | test('normalize tilde paths with lib', function() {
70 | var spade = t.spade, fooRequire;
71 | spade.register('foo', { directories: { lib: './lib' }}); // register as a package
72 | spade.register('foo/main', 'return require;');
73 | fooRequire = spade.require('foo');
74 | equal(fooRequire.normalize('foo/~lib/main'), 'foo/main');
75 | equal(fooRequire.normalize('foo/~lib/core'), 'foo/core');
76 | });
77 |
78 | })();
79 |
--------------------------------------------------------------------------------
/tests/async-test.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2010 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license (see __preamble__.js)
5 | // ==========================================================================
6 | /*globals spade */
7 |
8 | (function() {
9 |
10 | var t = {}, Spade = spade.Spade;
11 |
12 | // ..........................................................
13 | // BASIC REQUIRE
14 | //
15 |
16 | module('spade: async require', {
17 | setup: function() {
18 | t.spade = new Spade();
19 |
20 | // preload a module
21 | t.spade.register('foo/baz', function(require,e) {
22 | e.id = 'foo/baz';
23 | e.async = require.async; // export for testing
24 | });
25 |
26 | // dummy loader loads only foo/bar on demand after delay
27 | t.spade.loader = {
28 |
29 | requests: 0,
30 |
31 | loadFactory: function(spade, id, done) {
32 | this.requests++;
33 | if (id === 'foo/bar') {
34 | setTimeout(function() {
35 | spade.register(id, function(r,e) { e.id='foo/bar'; });
36 | done();
37 | }, 10);
38 |
39 | } else {
40 | done('Not Found'); // immediately
41 | }
42 | }
43 | };
44 | },
45 |
46 | teardown: function() {
47 | delete t.spade;
48 | }
49 | });
50 |
51 | test('should not talk to loader if registered', function() {
52 | var spade = t.spade;
53 |
54 | stop(1000);
55 |
56 | spade.async('foo/baz', function(err) {
57 | start();
58 | equal(err, null);
59 | equal(spade.loader.requests, 0, 'loader should not have been called');
60 | equal(spade.require('foo/baz').id, 'foo/baz', 'should find foo');
61 | });
62 |
63 | });
64 |
65 | test('should let loader register', function() {
66 | var spade = t.spade;
67 | stop(1000);
68 | spade.async('foo/bar', function(err) {
69 | start();
70 | equal(err, null);
71 | equal(spade.loader.requests, 1, 'loader should have been called');
72 | equal(spade.require('foo/bar').id, 'foo/bar', 'should find foo');
73 | });
74 | });
75 |
76 |
77 | test('should normalize id', function() {
78 | var spade = t.spade;
79 | stop(1000);
80 | spade.async('/./foo/baz/../bar', function(err) {
81 | start();
82 | equal(err, null);
83 | equal(spade.loader.requests, 1, 'loader should have been called');
84 | equal(spade.require('foo/bar').id, 'foo/bar', 'should find foo');
85 | });
86 | });
87 |
88 |
89 | test('should expose async inside of module', function() {
90 | var spade = t.spade;
91 | stop(1000);
92 |
93 | var async = spade.require('foo/baz').async;
94 | ok(async, 'should have an async function');
95 |
96 | // normalize relative to async
97 | async('./bar', function(err) {
98 | start();
99 | equal(err, null);
100 | equal(spade.loader.requests, 1, 'loader should have been called');
101 | equal(spade.require('foo/bar').id, 'foo/bar', 'should find foo');
102 | });
103 | });
104 |
105 |
106 | test('should return err if loader does not register', function() {
107 | var spade = t.spade;
108 | stop(1000);
109 | spade.async('imaginary/bar', function(err) {
110 | start();
111 | equal(err, 'Not Found');
112 | equal(spade.loader.requests, 1, 'loader should have been called');
113 |
114 | raises(function() {
115 | spade.require('imaginary/bar');
116 | });
117 | });
118 |
119 | });
120 |
121 | })();
122 |
--------------------------------------------------------------------------------
/lib/spade.js:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Project: Spade - CommonJS Runtime
3 | // Copyright: ©2010 Strobe Inc. All rights reserved.
4 | // License: Licened under MIT license
5 | // ==========================================================================
6 | /*jslint evil:true */
7 | /*globals spade ARGS ARGV ENV __module ActiveXObject */
8 |
9 | (function() {
10 |
11 | var K, Sandbox, Sp, Evaluator, Ep, Loader, Lp, Spade, Tp;
12 |
13 |
14 | // ..........................................................
15 | // HELPER FUNCTIONS
16 | //
17 |
18 | K = function() {}; // noop
19 |
20 | // assume id is already normalized
21 | function packageIdFor(normalizedId) {
22 | return normalizedId.slice(0, normalizedId.indexOf('/'));
23 | }
24 |
25 | function remap(id, contextPkg) {
26 | var mappings = contextPkg ? contextPkg.mappings : null;
27 | if (!mappings) { return id; }
28 |
29 | var packageId = packageIdFor(id);
30 | if (mappings[packageId]) {
31 | id = mappings[packageId] + id.slice(id.indexOf('/'));
32 | }
33 | return id;
34 | }
35 |
36 | // convert a relative or otherwise de-normalized module id into canoncial form
37 | // normalize('./foo', 'bar/baz') -> 'bar/foo'
38 | // normalize('foo', 'bar/baz') -> 'foo/main' (or foo/~package is asPackage)
39 | // normalize('foo/bar', 'bar/baz') -> 'foo/bar'
40 | function normalize(id, contextId, contextPkg, _asPackage) {
41 | var idx, len;
42 |
43 | // slice separator off the end since it is not used...
44 | if (id[id.length-1]==='/') { id = id.slice(0,-1); }
45 |
46 | // need to walk if there is a .
47 | if (id.indexOf('.')>=0) {
48 | var parts = contextId && (id.charAt(0) ==='.') ? contextId.split('/') : [],
49 | part, next,
50 | packageName = parts[0],
51 | needsCleanup = false;
52 |
53 | idx = 0;
54 | len = id.length;
55 |
56 | if (contextPkg && contextPkg.main && contextId === packageName+'/main') {
57 | // If we're requiring from main we need to handle relative requires specially
58 | needsCleanup = true;
59 | parts = contextPkg.main.replace(/^\.?\//, '').split('/');
60 | }
61 |
62 | parts.pop(); // get rid of the last path element since it is a module.
63 |
64 | while(idx