lib/condensation/loaders/particle-loader.js
var EventEmitter = require('events').EventEmitter;
var _ = require('lodash');
var cUtil = require("../util");
var glob = require('glob');
var path = require('path');
var slash = require('slash');
var util = require('util');
var VError = require('verror');
var ParticleLoader = function(options) {
this.root = options.root;
this.registry = {};
this.typeMap = {
'asset': 'assets',
'condition': 'conditions',
'frontLoader': 'front_loaders',
'helper': 'helpers',
'mapping': 'mappings',
'metadata': 'metadata',
'output': 'outputs',
'parameter': 'parameters',
'partial': 'partials',
'resource': 'resources',
'set': 'sets',
'template': 'cftemplates'
};
this.typeGlobs = {
'asset': ['','?(.hbs)]'],
'condition': ['','.*'],
'frontLoader': ['.js'],
'helper': ['.js'],
'mapping': ['','.*'],
'metadata': ['','.*'],
'output': ['','.*'],
'parameter': ['','.*'],
'partial': ['','.*'],
'resource': ['','.*'],
'set': ['','.*'],
'template': ['?(.hbs)']
};
};
util.inherits(ParticleLoader,EventEmitter);
ParticleLoader.prototype.loadParticle = function(type,cModule,pPath,options) {
var loadError = new VError("particle load error");
var opts = _.merge({
parentFile: null,
cModule: ''
},_.omit(options,_.isUndefined));
if (!_.isString(pPath)) {
throw new VError(loadError, '%s particlePath in "%s" must be a string', type, opts.parentFile.path);
}
// Make Windows Happy
var particlePath = pPath.replace(/\//g,path.sep);
// Used to reduce the number of times this particle is searched for.
// TODO Possible to group by package instead of file?
var particleId = [opts.parentFile.path,cModule,particlePath].join(':');
this.registry[type] = this.registry[type] || {};
if (this.registry[type][particleId]) {
return this.registry[type][particleId];
}
// Start building the particle object
var particle = { modulePath: '' };
// Look for the closest particles directory in relation to the parent file making the request
var particlesIndex = opts.parentFile.path.lastIndexOf(path.join(path.sep,'particles',path.sep));
if (particlesIndex > 0) {
particle.modulePath = opts.parentFile.path.slice(0,particlesIndex);
}
// Look in node_modules is a condensation module is defined
if (cModule) {
particle.modulePath = path.join(particle.modulePath,'node_modules',cModule);
}
// This feels UGLY
// Walks the tree to find particles in parent node_modules directories
// TODO Got to be a better way to do this.
var done = false;
while (done === false) {
particle = _.merge(particle,this.genParticlePaths(particle,type,particlePath));
// TODO more efficient discovery?
var globFiles = _.map(this.typeGlobs[type], function(pattern) {
return glob.sync(particle.path+pattern,{nodir: true});
});
var files = _.flatten(globFiles);
if (files[0]) {
particle.fsPath = files[0];
done = true;
}
else {
var lastNodeModules = particle.modulePath.lastIndexOf(path.join(path.sep,'node_modules'));
if (lastNodeModules < 0) {
done = true;
}
else {
particle.modulePath = particle.modulePath.slice(0,lastNodeModules);
var newNodeModules = particle.modulePath.lastIndexOf(path.join(path.sep,'node_modules'));
if (newNodeModules < 0) {
done = true;
}
else {
particle.modulePath = particle.modulePath.slice(0,newNodeModules+13);
particle.modulePath = path.join(particle.modulePath,cModule);
}
}
}
}
if (!particle.fsPath) {
throw new VError(loadError, '%s %s refrenced in %s could not be found', type, particlePath, opts.parentFile.path);
}
particle.format = cUtil.detectFormat({
particle: particle
});
this.registry[type][particleId] = particle;
this.emit('particle',particle);
return particle;
};
ParticleLoader.prototype.genParticlePaths = function(particle,type,particlePath) {
var pathObj = path.parse(path.join(particle.modulePath,'particles',this.typeMap[type],particlePath));
var realPath = path.format(pathObj);
var relativePathObj = path.parse(path.relative(path.resolve(process.cwd(),this.root),realPath));
var relativePath = path.format(relativePathObj);
var urlPath = slash(relativePath);
return {
pathObj: pathObj,
path: realPath,
relativePathObj: relativePathObj,
relativePath: relativePath,
urlPath: urlPath
};
};
ParticleLoader.prototype.processablePaths = function() {
var templatePaths = _.values(this.registry.template);
var assetPaths = _.values(this.registry.asset);
return _.map(_.flatten([templatePaths,assetPaths]),'path');
};
module.exports = ParticleLoader;