UNPKG

burn

Version:

Super lightweight JS module/asset loader

267 lines (232 loc) 7.26 kB
/* * BurnJS Loader * * Browser-side loader for our lightweight AMD and * Node exports implementation. * * This file is useless on its own, and is used in * conjunction with the browser pack compiler */ ;(function(config) { /* * Small assertion function * Seriously, this shouldn't need to be defined. Fucking JS. */ var assert = function(condition, msg) { msg = msg ? msg : "Uncaught assertion" if (!condition) { throw new Error(msg); } } /* * Small logger function */ var log = function(msg) { window.console && console.log(msg); } // Imported module instances var __CACHE__ = {}; /* * Create new module scope */ var newModuleScope = function(module_name, factory) { var self = this; self.module = {'exports':{}}; self.module_name = module_name /* * Include loader * * An alternative (but optional) loader for modules * which are incompatible with the AMD/CommonJS style * loaders. * * This is NOT a shim, it does not try and do anything * clever, it just runs the module with `window` as `this` * context and exports/modules/require/define not * exposed as either locals or globals. * * XXX: In future inspect the window object before/after * inclusion to determine if anything has been * added/removed from global namespace. * * @param name: (string) Module name */ var include = function(name) { var scope = new Object(); scope.amd = false; scope.ready = false; assert((name in config.modules), "No such module: "+name); if (name in __CACHE__) { assert(!scope.amd, "Cannot require.include() on preloaded AMD module: "+name); throw new Error("Cannot require.include() twice on the same module: "+name); } __CACHE__[name] = scope; var factory = config.modules[name]; factory.call(window); scope.ready = true; } /* * Asset loader * * Used to fetch assets from our bundle */ var assets = function(name) { this.get = function(name) { assert(name in config.assets, "No such asset: "+name); return config.assets[name]; } this.all = function() { return config.assets; } return this; } /* * Lightweight implementation of AMD.define() * https://github.com/amdjs/amdjs-api/wiki/AMD#define * * This is only a partial implementation because we * do not need all the functionality of AMD, we only * need to be able to simulate it. If you need full * functionality, then use r.js/almond.js/browserify */ var define = function() { // default arguments var id = self.module_name; var deps = ['require', 'module', 'exports']; var factory = null; // optional arguments as per AMD spec if (arguments.length == 3) { id = arguments[0]; deps = arguments[1]; factory = arguments[2]; } else if (arguments.length == 2) { deps = arguments[0]; factory = arguments[1]; } else if (arguments.length == 1) { factory = arguments[0]; } // sanity checks assert(typeof id === 'string', "id expected string"); assert(typeof deps === 'object', "id expected object"); assert(typeof factory === 'function', "factory expected function"); // fetch deps var requiredDeps = []; deps.forEach(function(name) { requiredDeps.push(require(name)); }); // create instance self.module.exports = factory.apply(self.module.exports, requiredDeps); } // fix define to declare AMD support define.amd = true; /* * Lightweight implementation of AMD.require() * https://github.com/amdjs/amdjs-api/wiki/require */ var require = function() { // HANDLES: require(Array, Function) if (arguments.length === 2) { // handle args var deps = arguments[0]; var factory = arguments[1]; assert(typeof deps === 'object', "Expected deps to be object"); assert(typeof factory === 'function', "Expected factory to be object"); // fetch deps var requiredDeps = []; deps.forEach(function(name) { requiredDeps.push(require(name)); }); // call factory return factory.apply(self.module.exports, requiredDeps); } // HANDLES: require(String) else if (arguments.length === 1) { // handle args var name = arguments[0]; assert(typeof name === 'string', "Expected name to be string"); // handle reserved names switch(name) { case 'define': return define; case 'require': return require; case 'exports': return self.module.exports; case 'module': return self.module; case 'assets': return new assets(); case 'include': return include; } // return instance var source_module = module_name; return new importModule(name); } else { throw new Error("require() invalid args") } }; // expose some helpers //require.asset = asset; //require.include = include; // expose some globals self.require = require; factory.call(window, require, self.module, self.module.exports, define); return self; } /* * Import module from bundle * * If module is already imported then return cached * instance, otherwise create and return new instance. * * @param name: (string/object) Module name or list of names * @returns: Module instance or list of instances */ var importModule = function(name) { // sanity checks var self = this; var module_name = name; assert((module_name in config.modules), "No such module: "+module_name); // use cached instance var m = __CACHE__[module_name]; if (m) { if (!m.ready) { throw new Error("Circular dependancy error: "+module_name); } if (!m.amd) { return; } return m.scope.module.exports; } // register in cache __CACHE__[module_name] = self; log("burn: module import: "+name); // create local scope var factory = config.modules[module_name]; // used to catch circular dependancies self.ready = false; self.amd = true; // create new module scope self.scope = new newModuleScope(name, factory); self.ready = true; return self.scope.module.exports; }; /* * Entry point loader */ (function() { // ensure require/define are not already specified assert(typeof require == "undefined", "require() already implemented"); assert(typeof define == "undefined", "define() already implemented"); config.entry_order.forEach(function(name) { var factory = config.entry[name]; new newModuleScope(name, factory); }); // attach to window as magic var factory = function() { } var scope = new newModuleScope('__WINDOW__', factory); window.require = scope.require; window.define = scope.define; })() })(__CONFIG__);