Skip to content

Commit

Permalink
fixes yandex#14. add possibility to create multiple storages
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexej Yaroshevich committed Nov 11, 2013
1 parent b78c053 commit ea655c0
Showing 1 changed file with 70 additions and 41 deletions.
111 changes: 70 additions & 41 deletions modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,25 @@ var DECL_STATES = {
RESOLVED : 'RESOLVED'
},

curOptions = {
trackCircularDependencies : true,
allowMultipleDeclarations : true
// to take a copy we need generator
genCurOptions = function () {
return {
trackCircularDependencies : true,
allowMultipleDeclarations : true
};
},

undef,
modulesStorage = {},
declsToCalc = [],
waitForNextTick = false,
pendingRequires = [],

genContext = function () {
return {
modulesStorage : {},
declsToCals : [],
waitForNextTick : false,
pendingRequires : [],
curOptions : genCurOptions()
};
},

/**
* Defines module
Expand All @@ -40,21 +49,21 @@ var DECL_STATES = {
deps = [];
}

var module = modulesStorage[name];
var module = this.modulesStorage[name];
if(module) {
if(!curOptions.allowMultipleDeclarations) {
if(!this.curOptions.allowMultipleDeclarations) {
throwMultipleDeclarationDetected(name);
return;
}
}
else {
module = modulesStorage[name] = {
module = this.modulesStorage[name] = {
name : name,
decl : undef
};
}

declsToCalc.push(module.decl = {
this.declsToCalc.push(module.decl = {
name : name,
fn : declFn,
state : DECL_STATES.NOT_RESOLVED,
Expand All @@ -72,12 +81,12 @@ var DECL_STATES = {
* @param {Function} cb
*/
require = function(modules, cb) {
if(!waitForNextTick) {
waitForNextTick = true;
nextTick(onNextTick);
if(!this.waitForNextTick) {
this.waitForNextTick = true;
nextTick.call(onNextTick);
}

pendingRequires.push({
this.pendingRequires.push({
modules : modules,
cb : cb
});
Expand All @@ -89,7 +98,7 @@ var DECL_STATES = {
* @returns {String} state, possible values NOT_DEFINED, NOT_RESOLVED, IN_RESOLVING, RESOLVED
*/
getState = function(name) {
var module = modulesStorage[name];
var module = this.modulesStorage[name];
return module?
DECL_STATES[module.decl.state] :
'NOT_DEFINED';
Expand All @@ -101,7 +110,7 @@ var DECL_STATES = {
* @returns {Boolean}
*/
isDefined = function(name) {
return !!modulesStorage[name];
return !!this.modulesStorage[name];
},

/**
Expand All @@ -111,28 +120,28 @@ var DECL_STATES = {
setOptions = function(options) {
for(var name in options) {
if(options.hasOwnProperty(name)) {
curOptions[name] = options[name];
this.curOptions[name] = options[name];
}
}
},

onNextTick = function() {
waitForNextTick = false;
calcDeclDeps();
applyRequires();
this.waitForNextTick = false;
calcDeclDeps.call(this);
applyRequires.call(this);
},

calcDeclDeps = function() {
var i = 0, decl, j, dep, dependOnDecls;
while(decl = declsToCalc[i++]) {
while(decl = this.declsToCalc[i++]) {
j = 0;
dependOnDecls = decl.dependOnDecls;
while(dep = decl.deps[j++]) {
if(!isDefined(dep)) {
if(!isDefined.call(this, dep)) {
throwModuleNotFound(dep, decl);
break;
}
dependOnDecls.push(modulesStorage[dep].decl);
dependOnDecls.push(this.modulesStorage[dep].decl);
}

if(decl.prevDecl) {
Expand All @@ -141,32 +150,33 @@ var DECL_STATES = {
}
}

declsToCalc = [];
this.declsToCalc = [];
},

applyRequires = function() {
var requiresToProcess = pendingRequires,
var requiresToProcess = this.pendingRequires,
require, i = 0, j, dep, dependOnDecls, applyCb;

pendingRequires = [];
this.pendingRequires = [];

while(require = requiresToProcess[i++]) {
j = 0; dependOnDecls = []; applyCb = true;
while(dep = require.modules[j++]) {
if(!isDefined(dep)) {
if(!isDefined.call(this, dep)) {
throwModuleNotFound(dep);
applyCb = false;
break;
}

dependOnDecls.push(modulesStorage[dep].decl);
dependOnDecls.push(this.modulesStorage[dep].decl);
}
applyCb && applyRequire(dependOnDecls, require.cb);
applyCb && applyRequire.call(this, dependOnDecls, require.cb);
}
},

applyRequire = function(dependOnDecls, cb) {
requireDecls(
requireDecls.call(
this,
dependOnDecls,
function(exports) {
cb.apply(global, exports);
Expand All @@ -186,11 +196,11 @@ var DECL_STATES = {
--unresolvedDeclCnt;
}
else {
if(curOptions.trackCircularDependencies && isDependenceCircular(decl, path)) {
if(this.curOptions.trackCircularDependencies && isDependenceCircular(decl, path)) {
throwCircularDependenceDetected(decl, path);
}

decl.state === DECL_STATES.NOT_RESOLVED && startDeclResolving(decl, path);
decl.state === DECL_STATES.NOT_RESOLVED && startDeclResolving.call(this, decl, path);

decl.state === DECL_STATES.RESOLVED? // decl resolved synchronously
--unresolvedDeclCnt :
Expand All @@ -214,7 +224,7 @@ var DECL_STATES = {
},

startDeclResolving = function(decl, path) {
curOptions.trackCircularDependencies && (path = path.slice()).push(decl);
this.curOptions.trackCircularDependencies && (path = path.slice()).push(decl);
decl.state = DECL_STATES.IN_RESOLVING;
var isProvided = false;
requireDecls(
Expand Down Expand Up @@ -368,13 +378,32 @@ var DECL_STATES = {
};
})(),

api = {
define : define,
require : require,
getState : getState,
isDefined : isDefined,
setOptions : setOptions
};
bindCtx = Function.prototype.bind || function (ctx) {
var fn = this,
noop = function () {},
bound = function () {
return fn.apply(ctx, Array.prototype.slice.call(arguments));
};
noop.prototype = this.prototype;
bonud.prototype = new noop();
return bonud;
},

genApi = function () {
var ctx = genContext();
return {
define : bindCtx.call(define, ctx),
require : bindCtx.call(require, ctx),
getState : bindCtx.call(getState, ctx),
isDefined : bindCtx.call(isDefined, ctx),
setOptions : bindCtx.call(setOptions, ctx)
};
},

api = genApi();

// possibility to create multiple storages
api.create = genApi;

if(typeof exports === 'object') {
module.exports = api;
Expand Down

0 comments on commit ea655c0

Please sign in to comment.