├── .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