diff --git a/README.ru.md b/README.ru.md index cb6de60..1b0c557 100644 --- a/README.ru.md +++ b/README.ru.md @@ -77,3 +77,79 @@ modules.require( // module 'A' now resolved to a }); ```` + +####Дополнение к YModules#### + +В дополнение сделана возможность обработать возможность догрузки модуля по имени, если такого модуля в системе не зарегистированно. В функцию loadModules будет передан массив имен модулей, которые необходимо загрузить. После того как все модули будут загружены, необходимо вызвать функцию обратного вызова. + +````javascript +modules.setOptions({ + /** + * Функция поиска модуля + * + * @param {String} moduleName Имя модуля, который не зарегистирован в системе + * @return {Boolean} Можно ли этот модуль загрузить + */ + findDep: function( moduleName ) { + // body... + }, + /** + * Функция срабатывает, если функция findDep вернула true. + * + * @param {Array} moduleNames Массив имен модулей, которые необходимо загрузить + * @param {Function} callback Функция обратного вызова + */ + loadModules: function( moduleName, callback ) { + // body... + } +}); +```` + +####Пример с загрузкой модуля#### + +Допустим, у нас есть внешняя утилита, которая умеет составлять объекты соотвествия имен модулей файлам в которых они находятся. В качестве загрузчка используется [LAB.js](http://labjs.com/). + +````javascript +var modulesDep = {"storage":"core.js", "menu": "ui.js"}; + +// Настройки LAB.js +$LAB.setGlobalDefaults({ + AllowDuplicates: false, + AlwaysPreserveOrder: true, + UseLocalXHR: false, + BasePath: '/javascripts/' +}); + +// Настраиваем поиск и загрузку незарегистрированных модулей +modules.setOptions({ + findDep: function( moduleName ) { + return modulesDep.hasOwnProperty(moduleName); + }, + loadModules: function( modulesNames, callback ) { + var + loadedCnt = 0, + filename, + i; + // end of vars + + for ( i = 0; i < modulesNames.length; i++ ) { + filename = modulesDep[modulesNames[i]]; + + (function(f) { + $LAB.script( getWithVersion(f) ).wait(function() { + loadedCnt++; + + if (loadedCnt === modulesNames.length) callback(); + }); + }(filename)); + } + } +}); + + +modules.require( + ['storage'], + function( s ) { + // module 'storage' now resolved to 's' + }); +```` diff --git a/modules.js b/modules.js index 2501d93..e964775 100644 --- a/modules.js +++ b/modules.js @@ -161,26 +161,45 @@ var undef, cb(exports); } }, - i = 0, len = unresolvedDepsCnt, - dep, decl; + unloadedDeps = [], + i = 0, j = 0, len = unresolvedDepsCnt, + dep, decl, - while(i < len) { - dep = deps[i++]; + require = function() { + + while(i < len) { + dep = deps[i++]; + if(typeof dep === 'string') { + decl = modulesStorage[dep].decl; + } + else { + decl = dep; + } + + decls.push(decl); + + startDeclResolving(decl, path, onDeclResolved); + } + }; + + while(j < len) { + dep = deps[j++]; if(typeof dep === 'string') { - if(!modulesStorage[dep]) { + if(!modulesStorage[dep] && typeof curOptions.findDep === 'function' && typeof curOptions.loadModules === 'function' && curOptions.findDep(dep)) { + unloadedDeps.push(dep); + } + else if(!modulesStorage[dep]) { cb(null, buildModuleNotFoundError(dep, fromDecl)); return; } - - decl = modulesStorage[dep].decl; - } - else { - decl = dep; } + } - decls.push(decl); - - startDeclResolving(decl, path, onDeclResolved); + if(unloadedDeps.length) { + curOptions.loadModules(unloadedDeps, require); + } + else { + require(); } }, diff --git a/modules/A.js b/modules/A.js new file mode 100644 index 0000000..74fb22c --- /dev/null +++ b/modules/A.js @@ -0,0 +1,37 @@ +/** + * @param {Object} modules + */ +!function ( modules, undefined ) { + 'use strict'; + + var + /** + * @param {Function} provide Async module export + */ + module = function ( provide ) { + var + /** + * // moduleRealization description + */ + moduleRealization = function() { + return 100; + }; + // end of vars + + provide(moduleRealization); + }; + // end of vars + + + /** + * @module A + * @version 0.1 + */ + modules.define( + 'A', // Module name + [], // Dependies + module // Module realization + ); +}( + ( this.hasOwnProperty('modules') ) ? this.modules : modules +); \ No newline at end of file diff --git a/modules/B.js b/modules/B.js new file mode 100644 index 0000000..c31af7f --- /dev/null +++ b/modules/B.js @@ -0,0 +1,40 @@ +/** + * @param {Object} modules + */ +!function ( modules, undefined ) { + 'use strict'; + + var + /** + * @param {Function} provide Async module export + */ + module = function ( provide, A, C ) { + var + resultA = A(), + resultC = C(), + + /** + * // moduleRealization description + */ + moduleRealization = function() { + return 100 + resultA + resultC; + }; + // end of vars + + provide(moduleRealization); + }; + // end of vars + + + /** + * @module B + * @version 0.1 + */ + modules.define( + 'B', // Module name + ['A', 'C'], // Dependies + module // Module realization + ); +}( + ( this.hasOwnProperty('modules') ) ? this.modules : modules +); \ No newline at end of file diff --git a/modules/C.js b/modules/C.js new file mode 100644 index 0000000..d6a2705 --- /dev/null +++ b/modules/C.js @@ -0,0 +1,37 @@ +/** + * @param {Object} modules + */ +!function ( modules, undefined ) { + 'use strict'; + + var + /** + * @param {Function} provide Async module export + */ + module = function ( provide ) { + var + /** + * // moduleRealization description + */ + moduleRealization = function() { + return 200; + }; + // end of vars + + provide(moduleRealization); + }; + // end of vars + + + /** + * @module C + * @version 0.1 + */ + modules.define( + 'C', // Module name + [], // Dependies + module // Module realization + ); +}( + ( this.hasOwnProperty('modules') ) ? this.modules : modules +); \ No newline at end of file diff --git a/test/test.js b/test/test.js index d7d573f..76ff093 100644 --- a/test/test.js +++ b/test/test.js @@ -71,6 +71,62 @@ describe('resolving', function() { }); }); +describe('load modules', function() { + it('should properly load module and resolve dependencies', function(done) { + var + modulesDep = {'A': './modules/A.js', 'B': './modules/B.js', 'C': './modules/C.js'}; + + // Настраиваем поиск и загрузку незарегистрированных модулей + modules.setOptions({ + findDep: function( moduleName ) { + return modulesDep.hasOwnProperty(moduleName); + }, + loadModules: function( modulesNames, callback ) { + var + fs = require('fs'), + vm = require('vm'), + loadedCnt = 0, + + // Proxy + originalRequire = require, + processProcess = process, + + filename, file, context, script, i; + + for(i = 0; i < modulesNames.length; i++) { + filename = modulesDep[modulesNames[i]]; + (function(filename){ + file = fs.readFile(filename, 'utf8', function(err, data) { + context = vm.createContext({ + process: processProcess, + require: originalRequire, + modules: modules, + console: console, + exports: exports, + module: { + exports: exports + } + }); + script = vm.createScript(data, filename); + script.runInNewContext(context); + + loadedCnt++; + + if (loadedCnt === modulesNames.length) callback(); + }); + }(filename)) + }; + } + }); + + modules.require(['B'], function(B) { + var res = B(); + res.should.have.been.equal(400); + done(); + }); + }); +}); + describe('errors', function() { it('should throw error on requiring undefined module', function(done) { modules.require(['A'], function() {}, function(e) {