123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- /**
- * The jxLoader main class which does all of the work of ordering files from various
- * repositories according to their dependencies.
- */
- //requires
- var yaml = require('js-yaml'),
- sys = require('sys'),
- fsp = require('promised-io/fs'),
- fs = require('fs'),
- path = require('path'),
- util = require('util'),
- Walker = require('walker');
- //check to see if mootools is already in the environment
- if (typeof MooTools == 'undefined') {
- require('mootools').apply(GLOBAL);
- sys.puts('loaded mootools');
- } else {
- sys.puts('mootools already loaded');
- }
- var jxLoader = new Class({
- Implements: [Events, Options],
- options: {},
- config: null,
- repos: null,
- flat: null,
- numRepos: 0,
- loadedRepos: 0,
- logger: null,
- debug: false,
- initialize: function (options) {
- this.setOptions(options);
- this.config = {};
- this.repos = {};
-
- this.logger = !nil(options.logger) ? options.logger : console;
- this.logger.info("Received logger in jxLoader");
- this.debug = nil(options.debug)? false : options.debug;
- },
- /**
- * Add a repository to the loader
- *
- * Paramaters:
- * config - The config should be an object that lists the appropriate keys for
- * the listed repository
- */
- addRepository: function (config) {
- this.config = Object.merge(this.config, {repos: config});
-
- this.numRepos = Object.getLength(this.config.repos);
-
- Object.each(this.config.repos, function(conf, key){
- if (nil(this.repos[key])) {
- this.loadRepository(key, conf);
- }
- },this);
- },
- loadRepository: function (key, config) {
- var p = config.paths.js,
- me = this;
- //walk the path and process all files we find...
- Walker(p).filterDir(function(dir){
- return !(dir.test('^\.[\S\s]*$','i'));
- }).on('file', function(file){
- try {
- var data = fs.readFileSync(file, 'utf-8');
-
- if (this.debug) sys.puts('File contents: ' + sys.inspect(data));
- //process the file
- var descriptor = {},
- regexp = /^-{3}\s*\n*([\S\s]*)\n+\.{3}/m, //regexp to get yaml contents
- matches = data.match(regexp);
- if (me.debug) me.logger.debug('All matches from getting yaml headers: ' + util.inspect(matches,false,null));
- if (!nil(matches)) {
- matches.shift();
- delete matches.index;
- delete matches.input;
- if (me.debug) me.logger.debug('matches is a ' + typeOf(matches));
- if (me.debug) me.logger.debug('Matches from getting yaml headers: ' + matches[0]);
- //remove \n from the string
- var str = matches[0].replace(new RegExp('\r','g'),'');
- if (me.debug) me.logger.debug('Matches from getting yaml headers after replacement: ' + str);
- try {
- descriptor = yaml.load(str);
- } catch (err) {
- me.logger.error('!!! error converting yaml');
- me.logger.error('YAML object: ' + util.inspect(yaml,false,null));
- me.logger.error('error: ' + util.inspect(err,false,null));
- throw err;
- }
- me.logger.debug('object returned from yaml eval = ' + util.inspect(descriptor,false,null));
-
-
- var requires = Array.from(!nil(descriptor.requires) ? descriptor.requires : []);
- var provides = Array.from(!nil(descriptor.provides) ? descriptor.provides : []);
- var optional = Array.from(!nil(descriptor.optional) ? descriptor.optional : []);
- var filename = path.basename(file);
- //normalize requires and optional. Fills up the default package name
- //if one is not present and strips version info
- requires.each(function(r, i){
- requires[i] = me.parse_name(key, r).join('/').replace(' ','');
- },this);
- optional.each(function(r, i){
- optional[i] = me.parse_name(key, r).join('/').replace(' ','');
- },this);
- if (nil(me.repos[key])) {
- me.repos[key] = {};
- }
- var index = String.uniqueID();
- me.repos[key][index] = Object.merge(descriptor,{
- repo: key,
- requires: requires,
- provides: provides,
- optional: optional,
- path: file
- });
-
- //make sure this is truly an object, not an array
- var obj = {};
- Object.each(me.repos[key][index], function(value, key){
- obj[key] = value;
- });
-
- me.repos[key][index] = obj;
- if (me.debug) me.logger.debug('Done processing ' + filename);
- } else {
- //there is no yaml header... drop this file
- me.logger.debug('no header for ' + file);
- }
- } catch (err) {
- me.logger.error('!!!err : ' + util.inspect(err,false,null));
- //do nothing, just finish up
- //sys.puts('no file ' + file);
- throw err;
- }
- return;
- })
- .on('end',function(){
- this.loadedRepos++;
- if (this.loadedRepos == this.numRepos) {
- this.fireEvent('loadRepoDone', [key]);
- }
- }.bind(this));
- },
- parse_name: function (def, name){
-
- var exploded = name.split('/');
- if (this.debug) this.logger.debug('exploded = ' + util.inspect(exploded,false,null));
- if (exploded.length == 1 ) {
- if (this.debug) this.logger.debug("returning from parse_name: " + util.inspect([def, exploded[0]],false,null));
- return [def, exploded[0]];
- }
- if (nil(exploded[0]) || exploded[0].length === 0) {
- if (this.debug) this.logger.debug("returning from parse_name: " + util.inspect([def, exploded[1]],false,null));
- return [def, exploded[1]];
- }
- var exploded2 = exploded[0].split(':');
- if (exploded2.length == 1) {
- if (this.debug) this.logger.debug("returning from parse_name: " + util.inspect(exploded,false,null));
- return exploded;
- }
- if (this.debug) this.logger.debug("returning from parse_name: " + util.inspect([exploded2[0],exploded[1]],false,null));
- return [exploded2[0],exploded[1]];
- },
- flatten: function (obj) {
- var flat = {};
- Object.each(obj, function(items, repo){
- Object.each(items, function(value, key){
- value.provides.each(function(val){
- val = val.replace(' ','');
- flat[repo.toLowerCase() + '/' + val.toLowerCase()] = value;
- },this);
- },this);
- },this);
- return flat;
- },
- getRepoArray: function () {
- return this.repos;
- },
- getFlatArray: function () {
- return this.flat;
- },
- compileDeps: function (classes, repos, type, opts, exclude) {
- opts = !nil(opts) ? opts : true;
- exclude = !nil(exclude) ? exclude : [];
- var list = [];
-
- if (nil(this.flat)) {
- this.flat = this.flatten(this.repos);
- }
- if (!nil(repos)) {
- sys.puts("in compileDeps... ready to get repo info");
- sys.puts("repos to get: " + util.inspect(repos, false, null));
- sys.puts("type: " + type);
- Array.from(repos).each(function(val){
- var o = {};
- o[val] = this.repos[val];
- var flat = this.flatten(o);
- Object.each(flat, function(obj, key){
- obj.visited = false;
- },this);
- Object.each(flat, function(obj, key){
- list = this.includeDependencies(val, key, opts, exclude, flat, list, type, [key]);
- },this);
- },this);
- }
- if (!nil(classes)) {
- classes.each(function(val){
- var r = this.findRepo(val);
- //clear visited reference
- Object.each(this.flat, function(obj, key){
- obj.visited = false;
- },this);
- list = this.includeDependencies(r, val, opts, exclude, this.flat, list, type);
- },this);
- }
- this.logger.info('list of dependencies: ' + util.inspect(list,false,null));
- return list;
- },
- compile: function (classes, repos, type, includeDeps, theme, exclude, opts) {
- type = !nil(type) ? type : 'js';
- includeDeps = !nil(includeDeps) ? includeDeps : true;
- theme = !nil(theme) ? theme : '';
- exclude = !nil(exclude) ? exclude : [];
- opts = !nil(opts) ? opts : true;
- if (nil(this.flat)) {
- this.flat = this.flatten(this.repos);
- }
-
- var deps,
- ret;
-
- this.logger.info("repos passed in: " + util.inspect(repos,false,null));
-
- if (includeDeps || !nil(repos)) {
- deps = this.compileDeps(classes, repos, type, opts, exclude);
- } else {
- deps = this.convertClassesToDeps(classes, type, exclude);
- }
- if (deps.length > 0) {
- var included = [],
- sources = [],
- ret2;
- if (type == 'js') {
- ret2 = this.getJsFiles(sources, included, deps);
- } else {
- ret2 = this.getCssFiles(sources, included, theme, deps);
- }
- ret = {
- included: ret2.includes,
- source: ret2.sources.join('\n\n')
- };
- } else {
- ret = false;
- }
- if (this.debug) this.logger.warn("returning: " + util.inspect(ret,false,null));
- return ret;
- },
- includeDependencies: function (repo, klass, opts, exclude, flat, list, type, ml) {
- klass = klass.contains('/') ? klass : repo.toLowerCase() + '/' + klass.toLowerCase();
- if (!Object.keys(flat).contains(klass)) {
- return list;
- }
- var inf = flat[klass];
- if ((inf.visited && ml.contains(klass)) ||
- (type=='js' && (exclude.contains(inf.path) || list.contains(inf.path))) ||
- (type=='css' && (exclude.contains(klass) || list.contains(klass))) ||
- (type=='jsdeps' && (exclude.contains(inf.path) || list.contains(klass)))) {
- return list;
- }
- var requires = Array.from(inf.requires);
- flat[klass].visited = true;
- if (opts && Object.keys(inf).contains('optional') && inf.optional.length > 0) {
- requires.combine(inf.optional);
- }
- if (requires.length > 0) {
- requires.each(function(req){
- var parts = req.split('/');
- if (nil(ml)) {
- ml = [];
- }
- ml.push(klass);
- list = this.includeDependencies(parts[0],parts[1],opts, exclude, flat, list, type, ml);
- ml.pop();
- },this);
- }
- if (type=='js') {
- list.push(inf.path);
- } else {
- list.push(klass);
- }
- return list;
- },
- convertClassesToDeps: function (classes, type, exclude) {
- var list;
- if (typeOf(classes) != 'array') {
- classes = Array.from(classes);
- }
- classes.each(function(klass){
- if (klass.contains('/')) {
- if (type=='js' && !exclude.contains(this.flat[klass.toLowerCase()].path)) {
- list.push(this.flat[klass.toLowerCase()].path);
- } else if (type=='css' && !exclude.contains(klass)) {
- list.push(klass);
- } else {
- Object.each(this.flat, function(arr, key) {
- var parts = key.split('/');
- if (parts[0].toLowerCase() == klass.toLowerCase()) {
- if (type=='js' && !exclude.contains(arr.path)) {
- list.push(arr.path);
- } else if (type=='css' && !exclude.contains(klass)) {
- list.push(key);
- }
- }
- },this);
- }
- }
- },this);
- return list;
- },
- findRepo: function(klass) {
- if (klass.contains('/')) {
- var parts = klass.split('/');
- return parts[0];
- } else {
- if (nil(this.flat)) {
- this.flat = this.flatten(this.repos);
- }
- var ret;
- Object.each(this.flat, function(arr, key){
- var parts = key.split('/');
- if (parts[1].toLowerCase() == klass.toLowerCase()) {
- ret = parts[0];
- }
- },this);
- return ret;
- }
- },
- getJsFiles: function (sources, included, deps) {
- this.logger.debug('list of dependencies: ' + util.inspect(deps,false,null));
- deps.each(function(filename){
- this.logger.debug('reading file: ' + filename);
- var s = fs.readFileSync(filename, 'utf-8');
- //Remove any tags
- if (this.options.tags !== null && this.options.tags !== undefined) {
- Array.from(this.options.tags).each(function(tag){
- var t = tag.escapeRegExp();
- re = new RegExp('//<' + t + '>[\\s\\S]*?//</' + t + '>','gi');
- s = s.replace(re,'');
- re = new RegExp('/\\*<' + t + '>\\*/[\\s\\S]*?/\\*</' + t + '>\\*/','gi');
- this.logger.debug('2nd regex = ' + util.inspect(re,false,null));
- s = s.replace(re,'');
- },this);
- }
- sources.push(s);
- included.push(filename);
- this.logger.debug('done reading file: ' + filename);
- },this);
- return {
- includes: included,
- sources: sources
- };
- },
- getCssFiles: function (sources, included, theme, deps) {
- deps.each(function(dep){
- var parts = dep.split('/');
- included.push(dep);
- if (!nil(this.config.repos[parts[0]].paths.css)) {
- var csspath = this.config.repos[parts[0]].paths.css;
- csspath = csspath.replace('{theme}',theme);
- csspath = fs.realpathSync(csspath);
- var cssfiles = !nil(this.flat[dep].css) ? this.flat[dep].css : '';
- if (cssfiles.length > 0) {
- cssfiles.each(function(css){
- var fp = csspath + '/' + css + '.css';
- if (path.existsSync(fp)) {
- var s = fs.readFileSync(fp, 'utf-8');
- if (this.options.rewriteImageUrl && !nil(this.config.repos[parts[0]].imageUrl)) {
- s = s.replace(new RegExp(this.config.repos[parts[0]].imageUrl, 'g'),this.options.imagePath);
- } else {
- this.logger.info('not updating urls in css file ' + css);
- }
- sources.push(s);
- } else {
- if (!nil(this.config.repos[parts[0]].paths.cssalt)) {
- var csspathalt = this.config.repos[parts[0]].paths.cssalt;
- csspathalt = csspathalt.replace('{theme}',theme);
- csspathalt = fs.realpathSync(csspathalt);
- fp = csspathalt + '/' + css + '.css';
- if (path.existsSync(fp)) {
- var s = fs.readFileSync(fp, 'utf-8');
- if (this.options.rewriteImageUrl && !nil(this.config.repos[parts[0]].imageUrl)) {
- s = s.replace(new RegExp(this.config.repos[parts[0]].imageUrl, 'g'),this.options.imagePath);
- } else {
- this.logger.info('not updating urls in css file ' + css);
- }
- sources.push(s);
- }
- }
- }
- },this);
- if (this.options.moveImages && !nil(this.flat[dep].images)) {
- var imageFiles = this.flat[dep].images;
- if (imageFiles.length > 0) {
- var ipath = this.config.repos[parts[0]].paths.images,
- imageLocation = this.config.repos[parts[0]].imageLocation;
- if (ipath.contains('{theme}')) {
- ipath = ipath.replace('{theme}', theme);
- }
- ipath = fs.realpathSync(ipath);
- //create destination if it's not already there
- if (!path.existsSync(imageLocation)) {
- fs.mkdirSync(imageLocation);
- }
- imageFiles.each(function(file){
- if (!path.existsSync(imageLocation + '/' + file)) {
- var inStr = fs.createReadStream(ipath + '/' + file),
- outStr = fs.createWriteStream(imageLocation + '/' + file);
- inStr.pipe(outStr);
- } else {
- this.logger.info('\t\tFile already exists');
- }
- },this);
- } else {
- this.logger.info('No image files to move');
- }
- } else {
- this.logger.info('Not moving image files');
- }
- }
- }
- },this);
-
- return {
- includes: included,
- sources: sources
- };
- }
- });
- exports.jxLoader = jxLoader;
|