Comparing version 0.3.1 to 0.4.0
@@ -29,26 +29,22 @@ 'use strict'; | ||
if (typeof fn == 'function') { | ||
if (!($inject = fn.$inject)) { | ||
$inject = []; | ||
fnText = fn.toString().replace(STRIP_NORMAL_COMMENTS, ''); | ||
argDecl = fnText.match(FN_ARGS); | ||
$inject = []; | ||
fnText = fn.toString().replace(STRIP_NORMAL_COMMENTS, ''); | ||
argDecl = fnText.match(FN_ARGS); | ||
_.each(argDecl[1].split(FN_ARG_SPLIT), function(arg) { | ||
var match = arg.match(MATCH_ARG_COMMENT); | ||
_.each(argDecl[1].split(FN_ARG_SPLIT), function(arg) { | ||
var match = arg.match(MATCH_ARG_COMMENT); | ||
if (match) { | ||
$inject.push(match[1]); | ||
return; | ||
} | ||
if (match) { | ||
$inject.push(match[1]); | ||
return; | ||
} | ||
arg = arg.replace(STRIP_COMMENTS, ''); | ||
arg = arg.replace(STRIP_COMMENTS, ''); | ||
arg.replace(FN_ARG, function(all, underscore, name) { | ||
$inject.push(name); | ||
}); | ||
}); | ||
fn.$inject = $inject; | ||
} | ||
} | ||
arg.replace(FN_ARG, function(all, underscore, name) { | ||
$inject.push(name); | ||
}); | ||
}); | ||
return $inject; | ||
}; |
@@ -27,2 +27,3 @@ 'use strict'; | ||
var Module = require('./module'); | ||
var ModuleContext = require('./moduleContext'); | ||
@@ -32,2 +33,3 @@ | ||
config: ModuleContext.createWrapped, | ||
registerLoader: Module.register, | ||
}; | ||
@@ -34,0 +36,0 @@ |
'use strict'; | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
@@ -10,2 +9,3 @@ | ||
var ModuleMapper = require('./moduleMapper'); | ||
var Module = require('./module'); | ||
@@ -32,7 +32,4 @@ var internals = {}; | ||
this.$modules = { | ||
null: { | ||
dependencies: [], | ||
} | ||
}; | ||
this.$modules = {}; | ||
this.initModule(new Module()); | ||
this.$baseDir = config.baseDir; | ||
@@ -46,16 +43,8 @@ this.$mm = new ModuleMapper(config); | ||
ModuleContext.prototype.addModule = function(name, module) { | ||
if (_.isObject(module) && module.constructor.name === 'Object') { | ||
_.extend(this.$modules[name].content, module); | ||
ModuleContext.prototype.initModule = function(moduleDef, content) { | ||
this.$modules[moduleDef.fullName] = moduleDef; | ||
if (content) { | ||
moduleDef.content = content; | ||
} | ||
else { | ||
this.$modules[name].content = module; | ||
} | ||
}; | ||
ModuleContext.prototype.initModule = function(name, content) { | ||
this.$modules[name] = { | ||
dependencies: [], | ||
content: content || {}, | ||
}; | ||
}; | ||
ModuleContext.prototype.require = function(name) { | ||
@@ -74,65 +63,24 @@ var cacheKeys = _.keys(require.cache); | ||
}; | ||
ModuleContext.prototype.getModule = function(callee, name) { | ||
name = this.$mm.resolve(name); | ||
ModuleContext.prototype.getModule = function(parent, moduleDef) { | ||
this.$modules[parent.fullName].addDependency(moduleDef); | ||
if (name in this.$modules) { | ||
this.$modules[callee].dependencies.push(name); | ||
return this.$modules[name].content; | ||
if (_.has(this.$modules, moduleDef.fullName)) { | ||
return this.$modules[moduleDef.fullName].content; | ||
} | ||
var modulePath = path.join(this.$baseDir, name + '.js'); | ||
moduleDef.load(parent, this); | ||
if (!fs.existsSync(modulePath)) { | ||
if (fs.existsSync(path.join(this.$baseDir, name, 'index.js'))) { | ||
return this.getModule(callee, path.join(name, 'index')); | ||
} | ||
try { | ||
var module = this.require(name); | ||
this.initModule(name, module); | ||
this.$modules[callee].dependencies.push(name); | ||
return module; | ||
} | ||
catch(e) { | ||
e.message = 'Cannot load module ' + name + ' (required by module ' + (callee || '<root>') + ').'; | ||
throw e; | ||
} | ||
if (!_.has(this.$modules, moduleDef.fullName)) { | ||
throw new Error('[r42] Module ' + moduleDef.fullName + ' was not loaded for an undefined reason.'); | ||
} | ||
this.initModule(name); | ||
this.$modules[callee].dependencies.push(name); | ||
var defineCalled = false; | ||
global.define = function(replace, module) { | ||
if (defineCalled === true) { | ||
throw new Error('[r42] ModuleContext: define must be called only once ' + | ||
'per module but was called twice in ' + name + '.'); | ||
} | ||
defineCalled = true; | ||
if (module === undefined) { | ||
module = replace; | ||
replace = {}; | ||
} | ||
if (!_.isFunction(module)) { | ||
throw new Error('[r42] Define\'s module should be provided as a function.'); | ||
} | ||
this.addModule(name, this.inject(name, replace, module)); | ||
}.bind(this); | ||
this.require(modulePath); | ||
if (!defineCalled) { | ||
throw new Error('[r42] ModuleContext: define not called by module ' + name + '.'); | ||
return moduleDef.content; | ||
}; | ||
ModuleContext.prototype.removeModule = function(moduleDef) { | ||
if (!_.has(this.$modules, moduleDef.fullName)) { | ||
return; | ||
} | ||
return this.$modules[name].content; | ||
this.$modules[moduleDef.fullName] = undefined; | ||
}; | ||
ModuleContext.prototype.removeModule = function(resolvedName) { | ||
delete this.$modules[resolvedName]; | ||
}; | ||
ModuleContext.prototype.inject = function(optName, replace, module) { | ||
ModuleContext.prototype.inject = function(parent, replace, module) { | ||
if (module === undefined) { | ||
@@ -148,6 +96,6 @@ module = replace; | ||
if (_.isFunction(module)) { | ||
return module.apply(global, internals.computeArgs(this, optName, replace, module)); | ||
return module.apply(global, this.computeArgs(parent, replace, module)); | ||
} | ||
var res = internals.computeArgs(this, optName, replace, module); | ||
var res = this.computeArgs(parent, replace, module); | ||
@@ -181,15 +129,14 @@ if (!_.isArray(module)) { | ||
var wrapper = { | ||
inject: _.bind(this.inject, this, null), | ||
inject: _.bind(this.inject, this, new Module()), | ||
dumpDeps: _.bind(this.dumpDeps, this), | ||
} | ||
this.initModule('r42', wrapper); | ||
var r42Module = new Module('r42'); | ||
r42Module.npm = true; | ||
this.initModule(r42Module, wrapper); | ||
} | ||
return this.$modules.r42.content; | ||
}; | ||
module.exports = ModuleContext; | ||
// Fill up internals | ||
internals.computeArgs = function(mc, optName, replace, module) { | ||
ModuleContext.prototype.computeArgs = function(parent, replace, module) { | ||
var deps = module; | ||
@@ -208,17 +155,12 @@ if (_.isFunction(deps)) { | ||
if (dep[0] === '$') { | ||
dep = path.join( | ||
path.dirname(optName), | ||
dep.substr(1) | ||
); | ||
} | ||
return new Module(parent, dep, this.$mm); | ||
}, this); | ||
return dep; | ||
}); | ||
return _.map(deps, function(val, done) { | ||
return this.getModule(optName, val, done); | ||
}, mc); | ||
return _.map(deps, function(dep) { | ||
return this.getModule(parent, dep); | ||
}, this); | ||
}; | ||
module.exports = ModuleContext; | ||
internals.markup = { | ||
@@ -235,2 +177,5 @@ root: '<root>', | ||
}, | ||
colorizeCircularModule: function(module) { | ||
return module; | ||
}, | ||
}; | ||
@@ -241,16 +186,21 @@ internals.coloredMarkup = {}; | ||
var chalk = require('chalk'); | ||
internals.coloredMarkup.root = chalk.gray(internals.markup.root); | ||
internals.coloredMarkup.newBranch = chalk.blue(internals.markup.newBranch); | ||
internals.coloredMarkup.lastBranch = chalk.blue(internals.markup.lastBranch); | ||
internals.coloredMarkup.continueBranch = chalk.blue(internals.markup.continueBranch); | ||
internals.coloredMarkup.circular = chalk.green(internals.markup.circular); | ||
internals.coloredMarkup.openPar = chalk.red(internals.markup.openPar); | ||
internals.coloredMarkup.closePar = chalk.red(internals.markup.closePar); | ||
internals.coloredMarkup.colorizeNpmModule = function(module) { | ||
return chalk.magenta(module); | ||
}; | ||
internals.coloredMarkup = { | ||
root: chalk.gray(internals.markup.root), | ||
newBranch: chalk.blue(internals.markup.newBranch), | ||
lastBranch: chalk.blue(internals.markup.lastBranch), | ||
continueBranch: chalk.blue(internals.markup.continueBranch), | ||
circular: chalk.green(internals.markup.circular), | ||
openPar: chalk.red(internals.markup.openPar), | ||
closePar: chalk.red(internals.markup.closePar), | ||
colorizeNpmModule: function(module) { | ||
return chalk.magenta(module); | ||
}, | ||
colorizeCircularModule: function(module) { | ||
return chalk.yellow(module); | ||
}, | ||
} | ||
} | ||
internals.dumpDeps = function(mc, options, module, prevs, branch, indent) { | ||
module = module || null; | ||
module = module || '<root>'; | ||
prevs = (prevs || []).concat(module); | ||
@@ -262,3 +212,3 @@ branch = branch || ''; | ||
var isNPM = !(module in mc.$modules); | ||
var isNPM = mc.$modules[module].npm; | ||
@@ -269,3 +219,3 @@ if (isNPM) { | ||
var res = branch + (module || activeMarkup.root) + '\n'; | ||
var res = branch + (module === '<root>' ? activeMarkup.root : module) + '\n'; | ||
@@ -288,3 +238,3 @@ if (isNPM) { | ||
if (options.colors) { | ||
dep = dep.yellow; | ||
dep = activeMarkup.colorizeCircularModule(dep); | ||
} | ||
@@ -291,0 +241,0 @@ |
'use strict'; | ||
var _ = require('lodash'); | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
@@ -18,2 +20,3 @@ function ModuleMapper(config) { | ||
this.$baseDir = config.baseDir; | ||
this.$paths = _.map(config.paths || {}, function(to, from) { | ||
@@ -36,2 +39,12 @@ return { | ||
// If our module is a directory containing an index.js, append 'index' to it | ||
try { | ||
var base = path.join(this.$baseDir, name); | ||
if (fs.statSync(base).isDirectory() && | ||
fs.existsSync(path.join(base, 'index.js'))) { | ||
name = path.join(name, 'index'); | ||
} | ||
} | ||
catch (e) {} | ||
return name; | ||
@@ -38,0 +51,0 @@ } |
{ | ||
"name": "r42", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "Dependency injection done right.", | ||
@@ -33,8 +33,8 @@ "author": { | ||
"grunt": "~0.4.1", | ||
"grunt-mocha-test": "~0.8.0", | ||
"chai": "~1.8.1", | ||
"sinon": "~1.7.3", | ||
"sinon-chai": "~2.4.0", | ||
"grunt-mocha-test": "~0.9.0", | ||
"chai": "~1.9.0", | ||
"sinon": "~1.8.1", | ||
"sinon-chai": "~2.5.0", | ||
"chalk": ">=0.4.0" | ||
} | ||
} |
@@ -174,2 +174,28 @@ ## r42 | ||
##### Other load mechanism | ||
r42 allows you to create your own loading modules & to load modules using a different policy than its own. By default, a require load mechanism & a json load mechanism are included by default in r42. | ||
To use a different load mechanism than the default one, you need to prefix its name by `plugin!` (eg: `require!module` or `json!module`). This implies that you have to use the `replacer` argument or r42 comments to load a plugin using a specific loader. | ||
**Examples:** | ||
With the json loader: | ||
```js | ||
define(function (/*! json!$config */ config) { | ||
// Here "config" contains ./config.json parsed into a JS structure | ||
}); | ||
``` | ||
With the require loader: | ||
```js | ||
define(function (/*! require!$externalModule */ externalModule) { | ||
// Here "externalModule" contains the value returned by calling require with | ||
// ./externalModule | ||
}); | ||
``` | ||
#### Special APIs | ||
@@ -200,2 +226,38 @@ | ||
##### Writing your own loader plugin | ||
You can write other loader plugins and register them to r42 to use them after that in your codebase. Here is how to do so: | ||
```js | ||
var r42 = require('r42'); | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var myYamlModule = require('myYamlModule'); | ||
r42.registerLoader('yaml', function (moduleDef, parent, mc) { | ||
// moduleDef contains information about the module that should be loaded | ||
// parent contains information about the module that required moduleDef | ||
// mc is the context object, it handles the modules' cache | ||
// mc.$baseDir is the root path of the project | ||
// moduleDef.name contains the path to the module from baseDir | ||
// (without any extension) | ||
var filePath = path.join(mc.$baseDir, moduleDef.name + '.yaml'); | ||
try { | ||
// The loading should be synchronous | ||
var yaml = fs.readFileSync(filePath, 'utf8'); | ||
yaml = myYamlModule.parse(yaml); | ||
// Init the module into the context | ||
mc.initModule(moduleDef, yaml); | ||
} catch (e) { | ||
// <module>.fullName contains the plugin's name + ! + the module's name | ||
e.message = '[r42:yaml] Cannot load module ' + moduleDef.fullName + | ||
' (required by module ' + parent.fullName + '): ' + e.message; | ||
throw e; | ||
} | ||
}); | ||
``` | ||
##### Printing dependencies | ||
@@ -249,2 +311,10 @@ | ||
#### 0.4.0 | ||
* Add plugin API | ||
* Add plugins | ||
* default (previous loading mechanism moved to a plugin) | ||
* require (force use of require, no define expected in module) | ||
* json (load JSON file - using require) | ||
#### 0.3.1 | ||
@@ -276,3 +346,2 @@ | ||
* Add the possibility to load files that are not in Javascript (plain text / JSON / YAML / ...) using plugins | ||
* Add a test API |
24423
10
460
344
3