Running "mochacov:cov" (mochacov) task Coverage

Coverage

93%
2179
2033
146

/Users/kpdecker/dev/walmart/lumbar/lib/build.js

100%
46
46
0
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 fu = require('./fileUtil'),
4 resources = require('./util/resources');
5
6/**
7 * Creates a list of all of the resources for the current module.
8 *
9 * Context state: module
10 *
11 * Plugin Calls:
12 * moduleResources
13 * fileFilter
14 * resourceList
15 */
161exports.loadResources = function(context, callback) {
17331 var plugins = context.plugins;
18
19331 function filterResource(resource) {
20569 resource = resources.cast(resource);
21
22569 if (exports.filterResource(resource, context)) {
23488 return resource;
24 }
25 }
26
27331 plugins.moduleResources(context, function(err, files) {
28331 if (err) {
291 return callback(err);
30 }
31
32330 var fileFilter = plugins.fileFilter(context) || /.*/;
33330 fu.fileList(files, fileFilter, function(err, files) {
34330 if (err) {
351 return callback(err);
36 }
37
38329 async.map(files, function(resource, callback) {
39514 var resourceContext = context.clone();
40514 resourceContext.resource = resource;
41514 plugins.resourceList(resourceContext, callback);
42 },
43 function(err, resources) {
44329 resources = _.flatten(resources);
45329 resources = _.map(resources, filterResource);
46898 resources = _.filter(resources, function(resource) { return resource; });
47329 callback(err, resources);
48 });
49 });
50 });
51};
52
53/**
54 * Filters a given resource for platform constraints, if specified.
55 */
561exports.filterResource = function(resource, context) {
57918 function check(value, singular, plural) {
582506 if (typeof singular !== 'undefined') {
59172 return singular.not ? singular.not !== value : singular === value;
602334 } else if (plural) {
6173 var ret = (plural.not || plural).reduce(function(found, filePlatform) {
62105 return found || filePlatform === value;
63 }, false);
6473 return plural.not ? !ret : ret;
65 }
662261 return true;
67 }
68
69918 function checkResource(resource) {
70922 return check(context.platform, resource.platform, resource.platforms)
71 && check(context.package, resource.package, resource.packages)
72 && check(!!context.combined, resource.combined);
73 }
74918 return checkResource(resource)
75 && (!resource.originalResource || checkResource(resource.originalResource));
76};
77
78
79/**
80 * Runs a set of resources through the resource plugin.
81 *
82 * Context state: module
83 *
84 * Plugin Calls:
85 * resource
86 */
871exports.processResources = function(resources, context, callback) {
88329 var plugins = context.plugins;
89
90329 async.map(resources, function(resource, callback) {
91461 var resourceContext = context.clone();
92461 resourceContext.resource = resource;
93461 plugins.resource(resourceContext, function(err, newResource) {
94461 if (newResource && newResource !== resource) {
95103 newResource.originalResource = resource;
96 }
97
98461 callback(err, newResource);
99 });
100 },
101 function(err, resources) {
102329 if (err) {
1031 return callback(err);
104 }
105
106788 callback(err, resources.filter(function(resource) { return resource; }));
107 });
108};
109

/Users/kpdecker/dev/walmart/lumbar/lib/config.js

85%
67
57
10
LineHitsSource
11var _ = require('underscore'),
2 fu = require('./fileUtil'),
3 path = require('path'),
4 vm = require('vm');
5
6/**
7 * Reads the RAW JSON for a lumbar config file.
8 */
91exports.readConfig = function(lumbarFile) {
1037 try {
1137 var data = '(' + fu.readFileSync(lumbarFile) + ')';
12
13 // Yes this is totally unsafe, but we don't want the strictness of pure JSON for our
14 // config files and if you are running an untrusted lumbar file you already have concerns.
1537 return vm.runInThisContext(data, lumbarFile);
16 } catch (err) {
170 var line;
180 try {
190 var esprima = require('esprima');
200 console.log(err.stack, esprima.parse(data));
21 } catch (err) {
220 if (err.lineNumber) {
230 line = ':' + err.lineNumber;
24 }
25 }
260 throw new Error('Failed to load config ' + lumbarFile + line + ': ' + err);
27 }
28};
29
30/**
31 *
32 * @name load
33 * @function This function loads the lumbar JSON file, and returns
34 * helper methods associated with accessing its specific data.
35 * @param {string} lumbarFile the location of the lumbar file.
36 * @return {Object}
37 */
381exports.load = function(lumbarFile) {
3929 fu.lookupPath('');
40
4129 var config = exports.readConfig(lumbarFile);
4229 fu.lookupPath(path.dirname(lumbarFile));
43
4429 return exports.create(config);
45};
46
471exports.create = function(config) {
48183 var packageList, moduleList;
49
50183 function loadPackageList() {
51183 if (!config.packages) {
52137 config.packages = { web: { name: '' } };
53 }
54
55183 packageList = _.keys(config.packages);
56 }
57183 function loadModuleList() {
58183 if (!config.modules) {
591 throw new Error('No modules object defined: ' + JSON.stringify(config, undefined, 2));
60 }
61182 moduleList = _.keys(config.modules);
62 }
63
64183 loadPackageList();
65183 loadModuleList();
66
67182 return {
68 /** @typedef {Object} The raw lumbar file as a JSON object. */
69 attributes: config,
70 loadPrefix: function() {
7153 return config.loadPrefix || '';
72 },
73
74 /**
75 *
76 * @name packageList
77 * @function This function returns the list of packages found
78 * in the lumbar file.
79 * @return {Array.<Object>} array of package(s).
80 */
81 packageList: function() {
8260 return packageList;
83 },
84
85 /**
86 *
87 * @name combineModules
88 * @function This functions checks to see if the package, pkg,
89 * is going to combine all its modules or not.
90 * @param {string} pkg the name of the package
91 * @return {boolean} is this package destined to be combined?
92 */
93 combineModules: function(pkg) {
941918 if (config && config.packages && config.packages[pkg]) {
951615 return config.packages[pkg].combine;
96 }
97303 return false;
98 },
99 platformList: function(pkg) {
100109 if (!pkg) {
10162 return config.platforms || [''];
102 } else {
10347 if (config.packages[pkg]) {
10447 return config.packages[pkg].platforms || this.platformList();
105 }
1060 return this.platformList();
107 }
108 },
109
110 moduleList: function(pkg) {
111258 return (config.packages[pkg] || {}).modules || _.keys(config.modules);
112 },
113
114 module: function(name) {
115523 var ret = config.modules[name];
116523 if (ret) {
117520 ret.name = name;
118 }
119523 return ret;
120 },
121 isAppModule: function(module) {
12276 var app = config.application;
12376 return (app && app.module) === (module.name || module);
124 },
125 scopedAppModuleName: function(module) {
12646 var app = config.application;
12746 if (this.isAppModule(module)) {
1284 return 'module.exports';
129 } else {
13042 var app = config.application;
13142 return app && app.name;
132 }
133 },
134
135 routeList: function(module) {
13628 return config.modules[module].routes;
137 },
138
139 serialize: function() {
1402 function objectClone(object) {
14119 var clone = object;
142
14319 if (object && object.serialize) {
144 // Allow specialized objects to handle themselves
1450 clone = object.serialize();
14619 } else if (_.isArray(object)) {
1471 clone = _.map(object, objectClone);
14818 } else if (_.isObject(object)) {
14912 clone = {};
15012 _.each(object, function(value, name) {
15115 clone[name] = objectClone(value);
152 });
153 }
154
155 // Collapse simple resources
15619 if (clone && clone.src && _.keys(clone).length === 1) {
1570 clone = clone.src;
158 }
159
16019 return clone;
161 }
162
1632 return objectClone(this.attributes);
164 }
165 };
166};
167
168

/Users/kpdecker/dev/walmart/lumbar/lib/context.js

94%
114
108
6
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 fs = require('fs'),
4 fu = require('./fileUtil'),
5 resources = require('./util/resources');
6
71function Context(options, config, plugins, libraries, event) {
82271 this._package = options.package;
92271 this._platform = options.platform;
102271 this._plugins = plugins;
112271 this.mode = options.mode;
122271 this.module = options.module;
132271 this.fileConfig = options.fileConfig;
142271 this.resource = options.resource;
152271 this.config = config;
162271 this.libraries = libraries || options.libraries;
172271 this.event = event || options.event;
18}
191Context.prototype = {
20 fileUtil: fu,
21
22 clone: function(options) {
232092 var ret = new Context(this, this.config);
242092 ret.parent = this;
252092 var prototype = Object.keys(Context.prototype);
262092 for (var name in this) {
2764420 if (this.hasOwnProperty(name) && prototype.indexOf(name) === -1) {
2837224 ret[name] = this[name];
29 }
30 }
312092 if (options) {
32293 _.extend(ret, options);
33293 ret._package = options.package || this._package;
34293 ret._platform = options.platform || this._platform;
35 }
362092 return ret;
37 },
38
39 fileNamesForModule: function(mode, moduleName, callback) {
4089 var context = this.clone();
4189 context.mode = mode;
4289 context.module = moduleName && this.config.module(moduleName);
4389 if (moduleName && !context.module) {
442 return callback(new Error('Unknown module "' + moduleName + '"'));
45 }
46
4787 this.plugins.outputConfigs(context, function(err, configs) {
4887 if (err) {
490 return callback(err);
50 }
51
5287 async.map(configs, function(config, callback) {
53113 var fileContext = context.clone();
54113 fileContext.fileConfig = config;
55113 fileContext._plugins.fileName(fileContext, function(err, fileName) {
56113 config.fileName = fileName;
57113 callback(err, config);
58 });
59 },
60 callback);
61 });
62 },
63
64 loadResource: function(resource, callback) {
65421 if (!callback) {
66 // if only single param, assume as callback and resource from context
670 resource = this.resource;
680 callback = resource;
69 }
70
71421 var fileInfo = {name: resource.hasOwnProperty('sourceFile') ? resource.sourceFile : resource.src};
72
73421 function loaded(err, data) {
74 /*jshint eqnull: true */
75421 if (err) {
764 if (!err.resourceLoadError) {
774 var json = '';
784 try {
79 // Output JSON for the resource... but protect ourselves from a failure masking a failure
804 resource = _.clone(resource.originalResource || resource);
814 delete resource.library;
824 delete resource.enoent;
834 json = '\n\t' + JSON.stringify(resource);
84 } catch (err) { /* NOP */ }
85
864 var errorWrapper = new Error('Failed to load resource "' + fileInfo.name + '"' + json + '\n\t' + (err.stack || err));
874 errorWrapper.stack = errorWrapper.message + ' ' + (err.stack || err);
884 errorWrapper.source = err;
894 errorWrapper.code = err.code;
904 errorWrapper.resourceLoadError = true;
914 err = errorWrapper;
92 }
934 callback(err);
944 return;
95 }
96417 fileInfo.inputs = data.inputs;
97417 fileInfo.generated = data.generated;
98417 fileInfo.noSeparator = data.noSeparator;
99417 fileInfo.ignoreWarnings = data.ignoreWarnings || resource.ignoreWarnings;
100417 fileInfo.content = data.data != null ? data.data : data;
101
102 // Ensure that we dump off the stack
103417 _.defer(function() {
104417 callback(err, fileInfo);
105 });
106 }
107
108421 if (typeof resource === 'function') {
109202 resource(this, loaded);
110219 } else if (resource.src) {
111 // Assume a file page, attempt to load
112206 fu.readFile(resource.src, loaded);
113 } else {
11413 loaded(undefined, {data: '', noSeparator: true, inputs: resource.dir ? [resource.dir] : []});
115 }
116
117421 return fileInfo;
118 },
119
120 outputFile: function(writer, callback) {
121144 var context = this;
122144 context.plugins.file(context, function(err) {
123144 if (err) {
1240 return callback(err);
125 }
126
127144 context.plugins.fileName(context, function(err, fileName) {
128144 if (err) {
1290 return callback(err);
130 }
131
132144 context.buildPath = (fileName.root ? '' : context.platformPath) + fileName.path + '.' + fileName.extension;
133144 context.fileName = context.outdir + '/' + context.buildPath;
134144 writer(function(err, data) {
135144 data = _.defaults({
136 fileConfig: context.fileConfig,
137 platform: context.platform,
138 package: context.package,
139 mode: context.mode
140 }, data);
141
142144 if (err) {
1433 fs.unlink(context.fileName, function() { /* NOP To Prevent warning */});
1443 data.error = err;
145 }
146144 context.event.emit('output', data);
147
148144 context.fileCache = undefined;
149144 callback(err, data);
150 });
151 });
152 });
153 },
154
155 get description() {
156547 var ret = 'package:' + this.package + '_platform:' + this.platform;
157547 if (this.mode) {
158350 ret += '_mode:' + this.mode;
159 }
160547 if (this.fileName) {
161115 ret += '_config:' + this.fileName;
162 }
163547 if (this.module) {
164329 ret += '_module:' + (this.module.name || this.module);
165 }
166547 if (this.resource) {
167 // TODO : Anything better for this?
16819 ret += '_resource:' + resources.source(this.resource);
169 }
170547 return ret;
171 },
172
1731988 get plugins() { return this._plugins; },
174
1755961 get package() { return this._package; },
1764329 get platform() { return this._platform; },
177 get platformPath() {
178156 return this.platform ? this.platform + '/' : '';
179 },
180
181 get combined() {
1821918 return this.config.combineModules(this.package);
183 },
184 get baseName() {
185231 if (!this.combined) {
186163 return this.module.name;
187 } else {
18868 return (this.config.attributes.packages[this.package] || {}).name || this.package;
189 }
190 },
191
192 get resources() {
193289 if (this.parent) {
1940 return this.parent.resources;
195 } else {
196289 return this._resources;
197 }
198 },
199 set resources(value) {
200362 if (this.parent) {
201326 delete this.parent;
202 }
203362 this._resources = value;
204 }
205};
206
2071module.exports = Context;
208

/Users/kpdecker/dev/walmart/lumbar/lib/fileUtil.js

93%
175
164
11
LineHitsSource
11var _ = require('underscore'),
2 EventEmitter = require('events').EventEmitter,
3 fs = require('fs'),
4 path = require('path'),
5 handlebars = require('handlebars'),
6 resources = require('./util/resources');
7
81const EMFILE_RETRY = 250;
9
101var fileCache = {};
11
121exports = module.exports = new EventEmitter();
13
141function cacheRead(path, exec, callback) {
15352 path = exports.resolvePath(path);
16
17352 var cache = fileCache[path];
18352 if (cache) {
19192 if (cache.data) {
2099 callback(undefined, cache);
21 } else {
2293 cache.pending.push(callback);
23 }
24192 return;
25 }
26
27160 cache = fileCache[path] = {
28 pending: [callback],
29 artifacts: {}
30 };
31
32160 exec(path, function _callback(err, data) {
33160 if (err && err.code === 'EMFILE') {
340 setTimeout(exec.bind(this, path, _callback), EMFILE_RETRY);
35 } else {
36160 if (err) {
372 delete fileCache[path];
38 }
39
40160 cache.data = data;
41160 cache.pending.forEach(function(callback) {
42253 callback(err, cache);
43 });
44160 exports.emit('cache:set', path);
45 }
46 });
47}
48
491exports.resetCache = function(filePath) {
50259 filePath = filePath && path.normalize(filePath);
51259 exports.emit('cache:reset', filePath);
52
53259 if (filePath) {
54177 filePath = exports.resolvePath(filePath);
55177 delete fileCache[filePath];
56 } else {
5782 fileCache = {};
58 }
59};
60
611var lookupPath;
621exports.resolvePath = function(pathName) {
63 // Poormans path.resolve. We aren't able to use the bundled path.resolve due to
64 // it throwing sync EMFILE errors without a type to key on.
651459 if (lookupPath
66 && (pathName[0] !== '/' && pathName.indexOf(':/') === -1 && pathName.indexOf(':\\') === -1)
67 && pathName.indexOf(lookupPath) !== 0) {
68974 return lookupPath + pathName;
69 } else {
70485 return pathName;
71 }
72};
731exports.makeRelative = function(pathName) {
74597 if (pathName.indexOf(lookupPath) === 0) {
75565 return pathName.substring(lookupPath.length);
76 } else {
7732 return pathName;
78 }
79};
80
811exports.lookupPath = function(pathName) {
82156 if (pathName !== undefined) {
8394 lookupPath = pathName;
8494 if (lookupPath && !/\/$/.test(lookupPath)) {
8538 lookupPath += '/';
86 }
87 }
88156 return lookupPath;
89};
90
911exports.stat = function(file, callback) {
92826 fs.stat(file, function(err, stat) {
93826 if (err && err.code === 'EMFILE') {
940 setTimeout(exports.stat.bind(exports, file, callback), EMFILE_RETRY);
95 } else {
96826 callback(err, stat);
97 }
98 });
99};
100
1011exports.readFileSync = function(file) {
10237 return fs.readFileSync(exports.resolvePath(file));
103};
1041exports.readFile = function(file, callback) {
105230 cacheRead(file, fs.readFile.bind(fs), function(err, cache) {
106230 callback(err, cache && cache.data);
107 });
108};
1091exports.readFileArtifact = function(file, name, callback) {
11061 cacheRead(file, fs.readFile.bind(fs), function(err, cache) {
11161 var artifacts = cache.artifacts;
11261 callback(err, {data: cache.data, artifact: artifacts[name]});
113 });
114};
1151exports.setFileArtifact = function(path, name, artifact) {
11627 path = exports.resolvePath(path);
117
11827 var cache = fileCache[path];
11927 if (cache) {
12027 cache.artifacts[name] = artifact;
121 }
122};
123
1241exports.readdir = function(dir, callback) {
12561 cacheRead(dir, fs.readdir.bind(fs), function(err, cache) {
12661 callback(err, cache && cache.data);
127 });
128};
129
1301exports.ensureDirs = function(pathname, callback) {
131245 var dirname = path.dirname(pathname);
132245 exports.stat(dirname, function(err) {
133245 if (err && err.code === 'ENOENT') {
134 // If we don't exist, check to see if our parent exists before trying to create ourselves
13542 exports.ensureDirs(dirname, function() {
13642 fs.mkdir(dirname, parseInt('0755', 8), function _callback(err) {
13742 if (err && err.code === 'EMFILE') {
1380 setTimeout(fs.mkdir.bind(fs, dirname, parseInt('0755', 8), _callback), EMFILE_RETRY);
139 } else {
140 // Off to the races... and we lost.
14142 callback(err && err.code === 'EEXIST' ? undefined : err);
142 }
143 });
144 });
145 } else {
146203 callback();
147 }
148 });
149};
150
1511exports.writeFile = function(file, data, callback) {
152136 exports.resetCache(file);
153
154136 exports.ensureDirs(file, function(err) {
155136 if (err) {
1560 return callback(err);
157 }
158
159136 fs.writeFile(file, data, 'utf8', function _callback(err) {
160136 if (err && err.code === 'EMFILE') {
1610 setTimeout(fs.writeFile.bind(fs, file, data, 'utf8', _callback), EMFILE_RETRY);
162 } else {
163136 callback(err);
164 }
165 });
166 });
167};
168
169/**
170 * Takes a given input and returns the files that are represented.
171 *
172 * pathname may be:
173 * a resource object
174 * a path on the file system
175 * an array of resources
176 */
1771exports.fileList = function(pathname, extension, callback, dirList, resource, srcDir) {
178886 if (_.isFunction(extension)) {
1795 callback = extension;
1805 extension = /.*/;
181 }
182
183886 if (_.isArray(pathname)) {
184307 var files = pathname;
185307 pathname = '';
186307 if (!files.length) {
187123 return callback(undefined, []);
188 }
189184 return handleFiles(false, undefined, _.uniq(files));
190579 } else if (!dirList) {
191418 if (pathname.src) {
1920 resource = resource || pathname;
1930 pathname = pathname.src;
194 }
195
196418 pathname = exports.resolvePath(pathname);
197 }
198579 if (resource && resource.src) {
199193 resource = _.clone(resource);
200193 delete resource.src;
201 }
202
203579 function handleFiles(dirname, err, files, srcDir) {
204242 if (err) {
2050 return callback(err);
206 }
207
208242 var ret = [],
209 count = 0,
210 expected = files.length,
211 prefix = pathname ? pathname.replace(/\/$/, '') + '/' : '';
212
213242 function complete(files, index) {
214614 count++;
215
216614 ret[index] = files;
217
218614 if (count === expected) {
219241 ret = _.flatten(ret);
220
221241 if (srcDir) {
22257 ret = ret.map(function(file) {
223124 file = resources.cast(file);
224124 file.srcDir = srcDir;
225124 return file;
226 });
227 }
228
229241 if (dirname) {
23057 ret.push(_.defaults({dir: dirname}, resource));
23157 ret = ret.sort(function(a, b) {
232241 return resources.source(a).localeCompare(resources.source(b));
233 });
234 }
235
236241 callback(undefined, ret);
237 }
238 }
239
240242 if (!files.length) {
2411 callback(undefined, []);
242 }
243
244242 files.forEach(function(file, index) {
245614 var fileResource = resource;
246614 if (file.src) {
247193 fileResource = resource || file;
248193 file = file.src;
249421 } else if (_.isObject(file)) {
25064 complete(file, index);
25164 return;
252 }
253
254550 exports.fileList(prefix + file, extension, function(err, files) {
255550 if (err) {
2560 callback(err);
2570 return;
258 }
259
260550 complete(files, index);
261 }, dirname, fileResource, srcDir);
262 });
263 }
264
265579 exports.stat(pathname, function(err, stat) {
266579 if (err) {
26769 if (err.code === 'ENOENT') {
26869 callback(undefined, [ _.extend({src: exports.makeRelative(pathname), enoent: true}, resource) ]);
269 } else {
2700 callback(err);
271 }
27269 return;
273 }
274
275510 if (stat.isDirectory()) {
27658 exports.readdir(pathname, function(err, files) {
27758 var _pathname = exports.makeRelative(pathname);
27858 handleFiles(_pathname, undefined, files, srcDir || _pathname);
279 });
280 } else {
281452 pathname = exports.makeRelative(pathname);
282
283452 var basename = path.basename(pathname),
284 namePasses = basename[0] !== '.' && basename !== 'vendor' && (!dirList || extension.test(pathname)),
285 ret = [];
286452 if (namePasses) {
287394 if (resource) {
288170 ret = [ _.defaults({src: pathname, srcDir: srcDir}, resource) ];
289224 } else if (srcDir) {
29071 ret = [ { src: pathname, srcDir: srcDir } ];
291 } else {
292153 ret = [ pathname ];
293 }
294 }
295452 callback(undefined, ret);
296 }
297 });
298};
299
300//accepts a template string or a filename ending in .handlebars
3011exports.loadTemplate = function(template, splitOnDelimiter, callback) {
30244 function compile(templateStr, callback) {
30333 try {
30433 if (splitOnDelimiter) {
30520 callback(null, templateStr.split(splitOnDelimiter).map(function(bit) {
30640 return handlebars.compile(bit);
307 }));
308 } else {
30913 callback(null, handlebars.compile(templateStr));
310 }
311 } catch (e) {
3121 callback(e);
313 }
314 }
31544 if (template.match(/\.handlebars$/)) {
31619 exports.readFileArtifact(template, 'template', function(err, data) {
31719 if (err) {
3181 return callback(err);
319 }
320
32118 if (data.artifact) {
32210 callback(undefined, data.artifact);
323 } else {
3248 compile(data.data.toString(), function(err, data) {
3258 if (!err) {
3268 exports.setFileArtifact(template, 'template', data);
327 }
3288 callback(err, data);
329 });
330 }
331 });
332 } else {
33325 compile(template, callback);
334 }
335};
336

/Users/kpdecker/dev/walmart/lumbar/lib/jsCombine.js

62%
64
40
24
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 FileMap = require('./util/file-map'),
4 fu = require('./fileUtil'),
5 ChildPool = require('child-pool');
6
71var uglify = new ChildPool(__dirname + '/uglify-worker', {logId: 'uglify-worker'});
8
91exports.combine = function(context, files, output, minimize, noSeparator, callback) {
10
11112 function outputIfCompleted() {
12372 if (completed >= files.length) {
13109 var lastEl,
14 map = new FileMap(output),
15 warnings = [],
16
17 tasks = [];
18
19109 _.each(content, function(el) {
20372 var content = el.content.toString();
21
22372 if (!noSeparator && (!lastEl || !lastEl.noSeparator) && map.content()) {
23114 map.add(undefined, '\n;;\n');
24 }
25
26372 map.add(el.name, content, el, el.generated);
27
28372 lastEl = el;
29 }, '');
30
31109 var inputs = [];
32109 content.forEach(function(el) {
33372 if (el.inputs) {
3496 inputs.push.apply(inputs, el.inputs);
35276 } else if (el.name) {
36178 inputs.push(el.name);
37 }
38 });
39109 inputs = _.unique(inputs);
40
41 // "Serialize" the data in the map
42109 tasks.push(function(callback) {
43109 callback(undefined, map.content());
44 });
45
46 // Minimize the content if flagged
47109 if (minimize) {
480 var uglifyConfig = context.config.attributes.uglify || {};
49
500 tasks.push(function(data, callback) {
510 uglify.send({
52 output: output,
53 data: data,
54 compressorOptions: uglifyConfig.compressor,
55 manglerOptions: uglifyConfig.mangler,
56 outputOptions: uglifyConfig.output,
57 sourceMap: context.options.sourceMap ? map.sourceMap() : undefined
58 },
59 function(err, data) {
600 if (err) {
610 return callback(err);
62 }
63
640 _.each(data.warnings, function(msg) {
650 var match = /(.*?)\s*\[.*:(\d+),(\d+)/.exec(msg);
660 if (match) {
670 var msg = match[1],
68 line = parseInt(match[2], 10),
69 column = match[3],
70 context = map.context(line, column);
71
720 if (context && (!context.fileContext || !context.fileContext.ignoreWarnings)) {
730 context.msg = msg;
740 warnings.push(context);
75 }
76 } else {
770 warnings.push({msg: msg});
78 }
79 });
80
810 if (data.sourceMap) {
82 // Remap the sourcemap output for the point that it is actually used for output
83 // We need to restore the source map here as uglify will remove the original
84 // Declaration
850 map.sourceMap = function() { return data.sourceMap; };
86 }
87
880 callback(err, data.data);
89 });
90 });
91 }
92
93 // Output the source map if requested
94109 var sourceMap = context.options.sourceMap;
95109 if (sourceMap) {
960 var inlineSourceMap = sourceMap === true;
97
980 tasks.push(function(data, callback) {
990 map.writeSourceMap({
100 mapDestination: !inlineSourceMap && (sourceMap + '/' + context.buildPath),
101 outputSource: inlineSourceMap,
102 callback: function(err) {
1030 if (inlineSourceMap) {
1040 data += '\n' + map.sourceMapToken();
105 }
1060 callback(err, data);
107 }
108 });
109 });
110 }
111
112 // Output step
113109 tasks.push(function(data, callback) {
114109 fu.writeFile(output, data, callback);
115 });
116
117 // Excute everything and return to the caller
118109 async.waterfall(tasks, function(err) {
119109 if (err) {
1200 callback(new Error('Combined output "' + output + '" failed\n\t' + err));
1210 return;
122 }
123
124109 callback(undefined, {
125 fileName: output,
126 inputs: inputs,
127 warnings: warnings
128 });
129 });
130 }
131 }
132112 var completed = 0,
133 content = [];
134
135112 files.forEach(function(resource) {
136375 var fileInfo = context.loadResource(resource, function(err) {
137375 if (err && callback) {
1383 callback(err);
1393 callback = undefined;
1403 return;
141 }
142
143372 if (callback) {
144372 completed++;
145372 outputIfCompleted();
146 }
147 });
148375 content.push(fileInfo);
149 });
150};
151

/Users/kpdecker/dev/walmart/lumbar/lib/libraries.js

94%
168
158
10
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 bower = require('bower'),
4 config = require('./config'),
5 fs = require('fs'),
6 fu = require('./fileUtil'),
7 path = require('path'),
8 resources = require('./util/resources');
9
101function Libraries(options) {
11152 this.options = options;
12152 this.mixins = [];
13152 this.configs = [];
14}
15
161Libraries.prototype.initialize = function(context, callback) {
17149 this.mixins = [];
18149 this.originalConfig = _.clone(context.config.attributes);
19
20149 function normalize(libraries) {
21298 if (_.isString(libraries)) {
222 return [libraries];
23 } else {
24296 return _.map(libraries, function (name) {
2583 if (_.isString(name)) {
2611 return path.normalize(name);
27 } else {
2872 return name;
29 }
30 });
31 }
32 }
33
34149 var commandLineLibraries = normalize(this.options.libraries || []),
35 configLibraries = normalize(context.config.attributes.libraries || context.config.attributes.mixins || []),
36 bowerLibraries = this.bowerLibraries(context) || [],
37
38 allLibraries = _.union(commandLineLibraries, configLibraries, bowerLibraries);
39
40149 delete context.config.attributes.mixins;
41
42149 async.forEachSeries(allLibraries, _.bind(this.load, this, context), callback);
43};
44
451Libraries.prototype.bowerLibraries = function(context) {
46150 try {
47150 fs.statSync(fu.resolvePath('bower.json'));
48
492 var bowerDir = bower.config.directory,
50 possibleModules = fs.readdirSync(bowerDir);
51
521 return possibleModules
53 .map(function(name) {
543 return path.normalize(path.join(bowerDir, name));
55 })
56 .filter(function(name) {
573 try {
583 fs.statSync(path.join(name, 'lumbar.json'));
592 return true;
60 } catch (err) {
61 /* NOP */
62 }
63 });
64 } catch (err) {
65149 context.event.emit('debug', err);
66 }
67};
68
691Libraries.prototype.load = function(context, libraryConfig, callback) {
70 // Allow mixins to be passed directly
7188 var root = libraryConfig.root,
72 configPath,
73 self = this;
74
75 // Or as a file reference
7688 if (!_.isObject(libraryConfig)) {
778 root = root || libraryConfig;
78
79 // If we have a dir then pull lumbar.json from that
808 try {
818 var stat = fs.statSync(fu.resolvePath(libraryConfig));
828 if (stat.isDirectory()) {
833 libraryConfig = libraryConfig + '/lumbar.json';
845 } else if (root === libraryConfig) {
85 // If we are a file the root should be the file's directory unless explicitly passed
865 root = path.dirname(root);
87 }
88 } catch (err) {
890 return callback(err);
90 }
91
928 configPath = fu.resolvePath(libraryConfig);
938 libraryConfig = config.readConfig(configPath);
94 }
95
96 // To make things easy force root to be a dir
9788 if (root && !/\/$/.test(root)) {
9829 root = root + '/';
99 }
100
10188 if (!libraryConfig.name) {
1024 return callback(new Error('Mixin with root "' + root + '" is missing a name.'));
103 }
104
10584 var mixins = libraryConfig.mixins,
106 toRegister = {};
10784 delete libraryConfig.mixins;
108
10984 function mapMixin(mixin, name) {
110 // Only register once, giving priority to an explicitly defined mixin
11164 if (!toRegister[name]) {
11263 toRegister[name] = {
113 serialize: function() {
1140 return {name: this.name, library: this.parent.name};
115 },
116 name: name,
117 attributes: mixin,
118 parent: libraryConfig,
119 root: root
120 };
121 }
122 }
123
124 // Read each of the mixins that are defined in the config
12584 _.each(mixins, mapMixin, this);
126
127 // Make mixin modules accessible as normal mixins as well
12884 _.each(libraryConfig.modules, mapMixin, this);
129
130 // After we've pulled everything in register
13184 _.each(toRegister, function(mixin, name) {
13263 this.mixins[name] = this.mixins[name] || [];
13363 var list = this.mixins[name];
13463 list.push(mixin);
135 }, this);
136
137 // Run all of the plugins that are concerned with this.
13884 libraryConfig.root = root;
13984 libraryConfig.path = configPath;
14084 context.loadedLibrary = libraryConfig;
14184 context.plugins.loadMixin(context, function(err) {
14284 delete libraryConfig.root;
143
144 // And then splat everything else into our config
14584 _.defaults(context.config.attributes, _.omit(context.loadedLibrary, 'name', 'path'));
146
14784 libraryConfig.serialize = function() {
1480 return { library: this.name };
149 };
150
15184 libraryConfig.root = root;
15284 self.configs.push(libraryConfig);
153
15484 callback(err);
155 });
156};
157
1581Libraries.prototype.findDecl = function(mixins, mixinName) {
15922 if (!mixinName.name) {
1602 mixinName = {name: mixinName};
161 }
162
16322 return _.find(mixins, function(mixinDecl) {
16426 return (mixinDecl.name || mixinDecl) === mixinName.name
165 && (!mixinDecl.library || mixinDecl.library === mixinName.library);
166 });
167};
168
1691Libraries.prototype.moduleMixins = function(module) {
170 // Perform any nested mixin lookup
171330 var mixins = _.clone(module.mixins || []),
172 processed = {};
173330 for (var i = 0; i < mixins.length; i++) {
174120 var firstInclude = mixins[i],
175 mixinConfig = firstInclude.name && firstInclude,
176 mixin = this.getMixin(firstInclude),
177 added = [i, 0];
178
179 // Save a config object off for propagation to included mixins
180118 if (mixinConfig) {
18152 mixinConfig = _.omit(mixinConfig, 'overrides', 'name', 'library');
182 }
183
184118 if (!mixin) {
1850 throw new Error('Unable to find mixin "' + ((firstInclude && firstInclude.name) || firstInclude) + '"');
186 }
187
188 // Check if we need to include any modules that this defined
189118 var processedName = mixin.name + '_' + (mixin.parent && mixin.parent.name);
190118 if (!processed[processedName]) {
19196 processed[processedName] = true;
192
19396 _.each(mixin.attributes.mixins, function(mixinInclude) {
194 // Apply any attributes that were applied to the mixin config here
19522 if (mixinConfig) {
1968 mixinInclude = mixinInclude.name ? _.clone(mixinInclude) : {name: mixinInclude};
1978 _.extend(mixinInclude, mixinConfig);
198 }
199
200 // Save the library that caused the include so we can lookup the root and reverse
201 // any overrides in the future.
20222 if (firstInclude.overrides) {
2034 mixinInclude.overrideLibrary = _.extend({root: mixin.parent.root}, firstInclude);
204 } else {
20518 mixinInclude.overrideLibrary = mixin.parent;
206 }
207
20822 if (!this.findDecl(mixins, mixinInclude)) {
20922 added.push(mixinInclude);
210 }
211 }, this);
212 }
213
214 // If we've found any new mixins insert them at the current spot and iterate
215 // over those items
216118 if (added.length > 2) {
21722 mixins.splice.apply(mixins, added);
21822 i--;
219 }
220 }
221
222 // Extend the module with each of the mixins content, giving priority to the module
223328 return _.map(mixins.reverse(), function(mixin) {
22496 var mixinConfig = mixin.name && mixin,
225 name = mixin;
22696 if (mixinConfig) {
22744 mixinConfig = _.clone(mixinConfig);
22844 delete mixinConfig.library;
22944 delete mixinConfig.container;
230 }
23196 mixin = _.extend(
232 {},
233 this.getMixin(name),
234 mixinConfig);
23596 if (!mixin.attributes) {
2360 throw new Error('Mixin "' + (name.name || name) + '" is not defined.');
237 }
238
239 // Save a distinct instance of the config for resource extension
24096 if (mixinConfig) {
24144 mixinConfig = _.clone(mixinConfig);
24244 delete mixinConfig.overrides;
24344 delete mixinConfig.name;
244 }
245
24696 return {
247 library: mixin,
248 mixinConfig: mixinConfig
249 };
250 }, this);
251};
252
2531Libraries.prototype.mapFiles = function(value, library, config) {
254182 var files = _.map(value, function(resource) {
255280 return this.mapFile(resource, library, config);
256 }, this);
257460 files = _.filter(files, function(file) { return file; });
258
259181 return files;
260};
2611Libraries.prototype.mapFile = function(resource, library, config) {
262 // If explicitly declared the resource library takes precedence
263331 if (_.isString(resource.library || resource.mixin)) {
2643 library = this.getConfig(resource.library || resource.mixin);
2653 if (!library) {
2661 throw new Error('Mixin "' + (resource.library || resource.mixin) + '" not found');
267 }
2682 delete resource.mixin;
269 }
270
271330 return resources.map(resource, library, config);
272};
273
2741Libraries.prototype.mapPathToLibrary = function(src, library) {
27539 return resources.pathToLibrary(src, library);
276};
277
2781Libraries.prototype.getMixin = function(name) {
279216 var mixins = (this.mixins && this.mixins[name.name || name]) || [],
280 library = name.library || name.container;
281216 if (mixins.length > 1 && !library) {
2821 throw new Error(
283 'Duplicate mixins found for "' + (name.name || name) + '"'
284 + _.map(mixins, function(mixin) {
2852 return ' parent: "' + mixin.parent.name + '"';
286 }).join(''));
287 }
288
289215 if (library) {
2909 if (name.name === undefined) {
2910 var found = _.find(this.configs, function(config) {
2920 return config.name === library;
293 });
2940 if (!found) {
2950 throw new Error('Unable to find library "' + library + '"');
296 }
2970 return found;
298 }
299
3009 var found = _.find(mixins, function(mixin) {
30117 return mixin.parent.name === library;
302 });
3039 if (found) {
3048 return found;
305 } else {
3061 throw new Error('Mixin named "' + name.name + '" not found in library "' + library + '"');
307 }
308206 } else if (mixins.length === 1) {
309206 return mixins[0];
310 }
311};
3121Libraries.prototype.getConfig = function(name) {
31313 return _.find(this.configs, function(config) { return config.name === name; });
314};
315
3161Libraries.prototype.mergeHash = function(hashName, input, mixin, output) {
31790 if (mixin[hashName]) {
318 // Close the value to make sure that we are not overriding anything
31911 if (!output[hashName] || output[hashName] === input[hashName]) {
3209 output[hashName] = _.clone(input[hashName] || {});
321 }
32211 _.each(mixin[hashName], function(value, key) {
32317 if (!input[hashName] || !(key in input[hashName])) {
32413 output[hashName][key] = value;
325 }
326 });
32711 return true;
328 }
329};
3301Libraries.prototype.mergeFiles = function(fieldName, input, mixinData, output, library) {
33144 if (mixinData[fieldName]) {
33210 mixinData = _.isArray(mixinData[fieldName]) ? mixinData[fieldName] : [mixinData[fieldName]];
333
33410 var configData = input[fieldName] || [];
33510 if (!output[fieldName] || configData === output[fieldName]) {
3368 output[fieldName] = _.clone(configData);
337 }
33810 if (!_.isArray(configData)) {
3392 configData = [configData];
340 }
34110 if (!_.isArray(output[fieldName])) {
3421 output[fieldName] = [output[fieldName]];
343 }
344
345 // Insert point is at the start of the upstream list, which we are
346 // assuming occurs at length postions from the end.
34710 _.each(mixinData, function(value) {
348 //Make the include relative to the mixin
34913 value = (library.root || '') + value;
350
35113 output[fieldName].splice(
352 output[fieldName].length - configData.length,
353 0,
354 {src: value, library: library});
355 });
356
35710 return true;
358 }
359};
360
3611module.exports = Libraries;
362

/Users/kpdecker/dev/walmart/lumbar/lib/lumbar.js

89%
86
77
9
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 ChildPool = require('child-pool'),
4 Context = require('./context'),
5 EventEmitter = require('events').EventEmitter,
6 fs = require('fs'),
7 stateMachine = require('./state-machine'),
8 WatchManager = require('./watch-manager');
9
101exports.build = require('./build');
111exports.fileUtil = require('./fileUtil');
121exports.plugin = require('./plugin').plugin;
131exports.combine = require('./jsCombine').combine;
141exports.config = require('./config');
15
16/**
17 *
18 * @name init
19 * @function This function initializes a Lumbar instance
20 * @param {string} lumbarFile The lumbarFile is the main
21 * file. Its responsible to define all the platforms,
22 * packages, modules, and templates for Lumbar to use.
23 * @param {Object} options supports the following options:
24 * packageConfigFile (string): name of the package config file.
25 * outdir (string): path to directory of where to output the files.
26 * minimize (boolean): Should we minimize the files?
27 * @return {Object.<Function>}
28 */
291exports.init = function(lumbarFile, options) {
30 // Clone so we can mutate in the use API
3130 options = _.clone(options || {});
3230 options.plugins = _.clone(options.plugins || []);
33
3430 function logError(err) {
3557 if (err) {
363 event.emit('error', err);
37 }
38 }
39
4030 var event = new EventEmitter(),
41 watch,
42 watchContext;
43
4430 function watchOutputHandler(status) {
45102 if (!watch) {
46 // We've been cleaned up but residuals may still exist, do nothing on this exec
4714 return;
48 }
49
5088 if (status.fileConfig.isPrimary) {
5136 delete status.fileConfig;
5252 } else if (status.fileConfig.isPrimary === false) {
53 // This config is directly linked to another meaning we don't want to watch on it as
54 // it will be rebuilt.
5511 return;
56 }
57
5877 var originalContext = watchContext;
5977 watch.moduleOutput(status, function() {
6035 if (watchContext !== originalContext) {
61 // Ignore builds that may have occured at the same time as a config file change (i.e. a branch switch)
620 return;
63 }
64
6535 stateMachine.loadPlatform(watchContext.clone(status), function(err, contexts) {
6635 if (err) {
670 return logError(err);
68 }
69
7035 stateMachine.buildContexts(contexts, logError);
71 });
72 });
73 }
74
7530 return _.extend(event, {
76 use: function(plugin) {
77 // Only has impact before exec
780 options.plugins.push(plugin);
79 },
80
81 moduleMap: function(packageName, callback) {
825 if (!callback) {
831 callback = packageName;
841 packageName = undefined;
85 }
86
875 stateMachine.loadConfig(lumbarFile, event, options, function(err, rootContext) {
885 if (err) {
891 return callback(err);
90 }
91
924 rootContext.mode = 'scripts';
934 stateMachine.loadPackages(rootContext, packageName, function(err, contexts) {
944 if (err) {
951 return callback(err);
96 }
97
983 async.forEach(_.keys(contexts), function(packageName, callback) {
993 var package = contexts[packageName];
1003 async.forEach(_.keys(package), function(platformName, callback) {
1014 var platform = package[platformName],
102 context = platform[0];
103
1044 rootContext.plugins.get('module-map').buildMap(context, function(err, map) {
1054 if (!err) {
1064 package[platformName] = map;
107 }
1084 callback(err);
109 });
110 },
111 callback);
112 },
113 function(err) {
1143 callback(err, contexts);
115 });
116 });
117 });
118 },
119
120 /**
121 *
122 * @name build
123 * @function This function builds out the package(s).
124 * @param {string} packageName the name of the package listed under
125 * 'packages' from the lumbarFile passed in during the call to init().
126 * @param {Function} callback the node process Function
127 */
128 build: function(packageName, modules, callback) {
12911 stateMachine.loadAndInitDir(lumbarFile, event, options, function(err, rootContext) {
13011 if (err) {
1310 if (!callback) {
1320 throw err;
133 }
1340 return callback(err);
135 }
136
13711 stateMachine.buildPackages(rootContext, packageName, modules, callback);
138 });
139 },
140 watch: function(packageName, modules, callback) {
14118 if (!fs.watch) {
1420 throw new Error('Watch requires fs.watch, introduced in Node v0.6.0');
143 }
144
14518 ChildPool.isBackground(true);
146
14718 watch = new WatchManager();
14818 watch.on('watch-change', function(info) {
14943 event.emit('watch-change', info);
150 });
151
15218 var self = this;
15318 stateMachine.loadAndInitDir(lumbarFile, event, options, function(err, rootContext) {
15418 if (err) {
1550 logError(err);
156 }
157
15818 if (!callback) {
15918 callback = modules;
16018 modules = undefined;
161 }
162
16318 watchContext = rootContext;
164
165 // Watch for changes in the config file
16623 var mixinPaths = _.filter(_.pluck(rootContext.libraries.configs, 'path'), function(path) { return path; });
16718 watch.configFile(lumbarFile, mixinPaths, function() {
1684 watchContext = undefined;
1694 self.watch(packageName, callback);
170 });
171
172 // If we have errored do not exec everything as it could be in an indeterminate state
17318 if (err) {
1740 return;
175 }
176
177 // Watch the individual components
17818 event.removeListener('output', watchOutputHandler);
17918 event.on('output', watchOutputHandler);
180
181 // Actual build everything
18218 var packages = packageName ? [packageName] : rootContext.config.packageList();
18318 packages.forEach(function(name) {
18422 stateMachine.buildPackages(rootContext, name, modules, logError);
185 });
186 });
187 },
188 unwatch: function() {
18914 event.removeListener('output', watchOutputHandler);
19014 if (watch) {
19114 watch.removeAllListeners();
19214 watch.reset();
19314 watch = undefined;
19414 watchContext = undefined;
195 }
196 }
197 });
198};
199

/Users/kpdecker/dev/walmart/lumbar/lib/plugin.js

99%
108
107
1
LineHitsSource
11var _ = require('underscore'),
2 path = require('path');
31const corePlugins = [
4 'mixin',
5 'styles-output', 'scripts-output', 'static-output',
6 'scope', 'router', 'template', 'inline-styles',
7 'coffee-script', 'stylus', 'handlebars',
8 'module-map', 'package-config', 'stylus-config',
9 'update-externals',
10 'server-scripts',
11 'inline-styles-resources', 'styles', 'scripts', 'static'
12];
131var fileUtils = require("/Users/kpdecker/dev/walmart/lumbar/lib/./fileUtil");
14
151var globalPlugins = {};
16
171exports.plugin = function(name, plugin) {
1820 globalPlugins[name] = plugin;
1920 plugin.id = name;
20};
21
221exports.plugin('module-map', require('./plugins/module-map'));
231exports.plugin('package-config', require('./plugins/package-config'));
241exports.plugin('router', require('./plugins/router'));
251exports.plugin('scope', require('./plugins/scope'));
261exports.plugin('stylus', require('./plugins/stylus'));
271exports.plugin('stylus-config', require('./plugins/stylus-config'));
281exports.plugin('coffee-script', require('./plugins/coffee-script'));
291exports.plugin('handlebars', require('./plugins/handlebars'));
301exports.plugin('inline-styles', require('./plugins/inline-styles'));
311exports.plugin('inline-styles-resources', require('./plugins/inline-styles-resources'));
321exports.plugin('mixin', require('./plugins/mixin'));
331exports.plugin('update-externals', require('./plugins/update-externals'));
341exports.plugin('template', require('./plugins/template'));
351exports.plugin('styles', require('./plugins/styles.js'));
361exports.plugin('server-scripts', require('./plugins/server-scripts.js'));
371exports.plugin('scripts', require('./plugins/scripts.js'));
381exports.plugin('static', require('./plugins/static.js'));
391exports.plugin('styles-output', require('./plugins/styles-output.js'));
401exports.plugin('scripts-output', require('./plugins/scripts-output.js'));
411exports.plugin('static-output', require('./plugins/static-output.js'));
42
431exports.create = function(options) {
44142 var plugins;
45142 var modes; // all registered modes
46142 var pluginModes; // map of modes and plugins scoped to the mode
47142 var modeAll; // plugins that are scoped to all modes
48
49142 function runPlugins(context, methodName, complete, failOver, noMode) {
503088 var len = 0,
51 pluginMode = pluginModes[context.mode] || [];
52
533088 return (function next(complete) {
54 /*jshint boss:true */
559151 var plugin;
569151 while (plugin = plugins[len++]) {
57 // if plugin shouldn't work with current mode, go to next
5859897 if (!noMode
59 && (!context.mode || pluginMode.indexOf(plugin) < 0)
60 && modeAll.indexOf(plugin) < 0) {
6126387 continue;
62 }
63
6433510 var method = plugin[methodName];
6533510 if (method) {
666765 if (complete) {
676625 process.nextTick(function() {
686625 method.call(plugin, context, next, complete);
69 });
706625 return;
71 } else {
72140 return method.call(plugin, context, next, complete);
73 }
74 }
75 }
76
77 // We're done, send data back
782386 if (complete) {
79 // async
80 // Clear out our stack under async mode to try to keep the stack somewhat sane.
812197 process.nextTick(function() {
822197 complete(undefined, failOver && failOver());
83 });
84 } else {
85 // sync
86189 return failOver && failOver();
87 }
88 })(complete);
89 }
90
91142 function registerPlugin(plugin) {
922793 var _plugin = globalPlugins[plugin] || plugin;
93
942793 var mode = _plugin.mode;
952793 if (mode) {
962654 if (_.isString(mode)) {
972091 mode = [mode];
98 }
992654 _.each(mode, function(_mode) {
1003217 if (mode === 'all') {
101 // allow plugins to contribute new modes and participate in all modes
1020 modeAll.push(_plugin);
103 } else {
1043217 if (modes.indexOf(_mode) < 0) {
105424 modes.push(_mode);
106424 pluginModes[_mode] = [];
107 }
1083217 pluginModes[_mode].push(_plugin);
109 }
110 });
111 } else {
112139 modeAll.push(_plugin);
113 }
1142793 plugins.push(_plugin);
1152793 plugins.sort(function(a, b) {
11639521 return (a.priority || 50) - (b.priority || 50);
117 });
118 }
119
120142 return {
121 get: function(name) {
122 // Find the plugin with this id, if one exists
12377 var plugin = plugins.reduce(function(plugin, left) {
1241463 return plugin.id === name ? plugin : left;
125 });
126
127 // If the plugin was not found do not return the last item in the reduce
12877 if (plugin.id === name) {
12975 return plugin;
130 }
131 },
132 use: function(plugin) {
13313 if (plugin.path || (_.isString(plugin) && !globalPlugins[plugin])) {
1341 var pluginPath = plugin.path || plugin;
1351 var options = plugin.options;
1361 try {
1371 plugin = require(pluginPath);
138 } catch (e) {
1391 plugin = require(path.resolve(process.cwd(), fileUtils.lookupPath()) + '/node_modules/' + pluginPath);
140 }
1411 if ('function' === typeof plugin) {
1421 plugin = plugin(options);
143 }
144 }
14513 registerPlugin(plugin);
146 },
147
148 initialize: function(config) {
149 // reset
150142 plugins = [];
151142 modes = []; // all registered modes
152142 pluginModes = {}; // map of modes and plugins scoped to the mode
153142 modeAll = []; // plugins that are scoped to all modes
154
155 // load the core plugins
156142 if (!options.ignoreCorePlugins) {
157139 corePlugins.forEach(registerPlugin);
158 }
159
160142 var self = this;
161142 function plugin(plugins) {
162284 if (plugins) {
16338 plugins.forEach(self.use, self);
164 }
165 }
166
167 // load command line plugins
168142 plugin(options.plugins);
169
170 // load lumbar.json plugins
171142 plugin(config.attributes.plugins);
172 },
173
174 loadMixin: function(context, complete) {
17584 runPlugins(context, 'loadMixin', complete, undefined, true);
176 },
177 loadConfig: function(context, complete) {
178141 runPlugins(context, 'loadConfig', complete, undefined, true);
179 },
180 outputConfigs: function(context, complete) {
181248 runPlugins(context, 'outputConfigs', complete, function() {
182 // Default to a one to one mapping for a given {platform, package, module, mode} combo
183246 return [ {} ];
184 });
185 },
186 modeComplete: function(context, complete) {
187184 runPlugins(context, 'modeComplete', complete);
188 },
189 fileName: function(context, complete) {
190257 runPlugins(context, 'fileName', complete);
191 },
192
193 fileFilter: function(context) {
194329 return runPlugins(context, 'fileFilter');
195 },
196 moduleResources: function(context, complete) {
197453 runPlugins(context, 'moduleResources', complete, function() {
198248 var module = context.module;
199248 return (module[context.mode] || []).slice();
200 });
201 },
202 resourceList: function(context, complete) {
2031028 runPlugins(context, 'resourceList', complete, function() { return [context.resource]; });
204 },
205
206 file: function(context, complete) {
207144 runPlugins(context, 'file', complete);
208 },
209 module: function(context, complete) {
210274 runPlugins(context, 'module', complete);
211 },
212 resource: function(context, complete) {
213825 runPlugins(context, 'resource', complete, function() { return context.resource; });
214 },
215 modes: function() {
21647 return modes;
217 }
218 };
219};
220

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/coffee-script.js

100%
15
15
0
LineHitsSource
11var CoffeeScript,
2 path = require('path'),
3 fu = require('../fileUtil'),
4 _ = require('underscore');
5
61module.exports = {
7 mode: 'scripts',
8 priority: 50,
9 resource: function(context, next, complete) {
10273 var resource = context.resource;
11273 if (/\.coffee$/.test(resource.src)) {
122 CoffeeScript = CoffeeScript || require('coffee-script');
13
142 next(function(err, resource) {
152 function generator(context, callback) {
16 // Load the source data
172 context.loadResource(resource, function(err, file) {
182 if (err) {
191 return callback(err);
20 }
21
22 // Update the content
231 callback(err, {
24 data: CoffeeScript.compile(file.content.toString()),
25 inputs: file.inputs
26 });
27 });
28 }
29
30 // Include any attributes that may have been defined on the base entry
312 if (!_.isString(resource)) {
322 _.extend(generator, resource);
33 }
342 complete(undefined, generator);
35 });
36 } else {
37271 next(complete);
38 }
39 }
40};
41

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/handlebars.js

91%
92
84
8
LineHitsSource
1/**
2 * Template Plugin : Includes handlebars templates associated with a given file
3 * when said file is imported.
4 *
5 * Config:
6 * root:
7 * templates:
8 * template: Defines the template that is used to output the template in the module. See consts below.
9 * precompile:
10 * Flag/hash that enable precompilation. Truthy will enable precompilation. A hash with
11 * the key name "template" will override the rendering template. (See the template value above.)
12 * cache: Name of the javascript object that templates will be assigned to.
13 * Defaults to `$AppModule.templates` if an app module exists, otherwise `templates`
14 *
15 * Mixins:
16 * The template plugin will mixin any special values directly, giving priority to the local version.
17 *
18 */
191var _ = require('underscore'),
20 handlebars = require('handlebars'),
21 resources = require('../util/resources'),
22 templateUtil = require('../templateUtil');
23
241handlebars.registerHelper('without-extension', function(str) {
251 return str.replace(/\.[a-zA-Z0-9]+$/, '');
26});
27
28
291const DEFAULT_TEMPLATE_TEMPLATE = "/* handsfree : {{{name}}}*/\n{{{templateCache}}}['{{{name}}}'] = {{handlebarsCall}}({{{data}}});\n";
30
311function ensureTemplateTemplates(context, complete) {
3240 if (!context.configCache.templateTemplate) {
3314 var templateTemplate = (context.config.attributes.templates && context.config.attributes.templates.template) || DEFAULT_TEMPLATE_TEMPLATE;
3414 context.fileUtil.loadTemplate(templateTemplate, false, function(err, compiled) {
3514 if (err) {
361 complete(err);
37 } else {
3813 context.configCache.templateTemplate = compiled;
3913 complete();
40 }
41 });
42 } else {
4326 complete();
44 }
45}
46
471function loadTemplate(src, resource, context, callback) {
4840 ensureTemplateTemplates(context, function(err) {
4940 if (err) {
501 return callback(err);
51 }
5239 var artifactType = 'template' + context.fileConfig.server;
5339 context.fileUtil.readFileArtifact(src, artifactType, function(err, cache) {
5439 if (err) {
550 callback(new Error('Failed to load template "' + src + '"\n\t' + err));
560 return;
57 }
58
5939 var artifact = cache.artifact || {},
60 data = artifact.data || cache.data.toString(),
61 attr = context.config.attributes,
62 templates = attr.templates || {},
63 appModule = context.config.scopedAppModuleName(context.module),
64 templateCache = (attr.templates && attr.templates.cache)
65 || attr.templateCache
66 || ((appModule ? appModule + '.' : '') + 'templates'),
67 template = context.configCache.templateTemplate;
68
69 // Figure out what this file is called. This could vary due to prefixing and overriding
7039 var name = context.libraries.mapPathToLibrary(src, resource.library);
7139 if (templates.root && name.indexOf(templates.root) === 0) {
724 name = name.substring(templates.root.length);
73 }
7439 name = templateUtil.escapeJsString(name);
75
76 // We have the template data, now convert it into the proper format
7739 if (!cache.artifact) {
7818 if (templates.precompile) {
792 var options = context.fileCache.precompileTemplates;
802 if (!options) {
812 context.fileCache.precompileTemplates = options = _.clone(templates.precompile);
822 if (templates.knownHelpers || options.knownHelpers) {
830 options.knownHelpers = (options.knownHelpers || templates.knownHelpers).reduce(
84 function(value, helper) {
850 value[helper] = true;
860 return value;
87 }, {});
88 }
892 if (context.fileConfig.server && templates.server) {
901 _.extend(options, templates.server);
91 }
92 }
932 try {
942 data = handlebars.precompile(data, options);
95 } catch (err) {
960 return callback(err);
97 }
98 } else {
9916 data = "'" + templateUtil.escapeJsString(data) + "'";
100 }
10118 context.fileUtil.setFileArtifact(src, artifactType, {data: data, template: template});
102 }
103
10439 callback(
105 undefined,
106 template({
107 name: name,
108 handlebarsCall: templates.precompile ? 'Handlebars.template' : 'Handlebars.compile',
109 templateCache: templateCache,
110 data: data
111 })
112 );
113 });
114 });
115}
116
1171module.exports = {
118 mode: 'scripts',
119 priority: 50,
120
121 loadMixin: function(context, next, complete) {
12284 var mixinTemplates = context.loadedLibrary.templates;
12384 if (mixinTemplates) {
12414 var templates = context.libraries.originalConfig.templates || {},
125 configTemplates = _.clone(context.config.attributes.templates || templates),
126 assigned = false;
127
12814 ['template', 'precompile', 'cache', 'root'].forEach(function(key) {
12956 if (_.has(mixinTemplates, key) && !_.has(templates, key)) {
13010 configTemplates[key] = mixinTemplates[key];
13110 assigned = true;
132 }
133 });
134
13514 if (_.has(mixinTemplates, 'knownHelpers')) {
1361 configTemplates.knownHelpers = (configTemplates.knownHelpers || []).concat(mixinTemplates.knownHelpers);
1371 assigned = true;
138 }
139
14014 if (assigned) {
1417 context.config.attributes.templates = configTemplates;
142 }
143 }
14484 next(complete);
145 },
146
147 resource: function(context, next, complete) {
148229 var resource = context.resource;
149
150229 if (/\.handlebars$/.test(resource.src) || resource.template) {
15131 var loadedTemplates = context.fileCache.loadedTemplates;
15231 if (!loadedTemplates) {
15327 loadedTemplates = context.fileCache.loadedTemplates = {};
154 }
155
15631 var generator = function(buildContext, callback) {
15728 var output = [],
158 inputs = [];
15928 context.fileUtil.fileList(resource.src, /\.handlebars$/, function(err, files) {
16028 if (err) {
1610 callback(err);
1620 return;
163 }
164
16528 function ignore(file) {
166121 return file.dir || loadedTemplates[resources.source(file)];
167 }
16828 function checkComplete() {
16967 if (inputs.length === files.length) {
170 // Sorting is effectively sorting on the file name due to the name comment in the template
17127 callback(undefined, {
172 inputs: inputs,
173 data: output.sort().join(''),
174 name: resource.src,
175 generated: true,
176 noSeparator: true,
177 ignoreWarnings: true
178 });
17927 return true;
180 }
181 }
182
18349 inputs = _.map(files.filter(ignore), function(input) { return input.src || input; });
18428 if (checkComplete()) {
1851 return;
186 }
187
18827 files.forEach(function(file) {
18960 if (ignore(file)) {
19020 return;
191 }
192
19340 var src = file.src || file;
19440 loadedTemplates[src] = true;
19540 loadTemplate(src, resource, context, function(err, data) {
19640 if (err) {
1971 return callback(err);
198 }
199
20039 output.push(data.data || data);
20139 inputs.push(src);
20239 checkComplete();
203 });
204 });
205 });
206 };
20731 generator.sourceFile = resource.src;
20831 complete(undefined, generator);
209 } else {
210198 next(complete);
211 }
212 }
213};
214

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/inline-styles-resources.js

100%
8
8
0
LineHitsSource
11var inlineStyles = require('./inline-styles');
2
31module.exports = {
4 mode: ['scripts', 'styles'],
5 priority: 80,
6
7 moduleResources: function(context, next, complete) {
8369 if (inlineStyles.isInline(context) && context.mode === 'styles') {
9 // Prevent stylesheet output if in inline mode
103 complete(undefined, []);
11366 } else if (inlineStyles.isInline(context)) {
126 next(function(err, scripts) {
136 complete(undefined, scripts.concat(context.module.styles || []));
14 });
15 } else {
16360 next(complete);
17 }
18 }
19};
20

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/inline-styles.js

94%
34
32
2
LineHitsSource
1/**
2 * Inline-Styles Plugin : Include stylesheet in javascript modules
3 *
4 * Config:
5 * root:
6 * styles:
7 * inline: Truthy to inline styles on build.
8 * inlineLoader: Javascript method used to load sheets on the client.
9 *
10 * Mixins:
11 * All fields may be mixed in. In the case of conflicts the local config wins.
12 */
131var _ = require('underscore');
14
151function isInline(context) {
161564 return (context.config.attributes.styles || {}).inline;
17}
18
191module.exports = {
20 isInline: isInline,
21 mode: ['scripts', 'styles'],
22 priority: 10,
23
24 loadMixin: function(context, next, complete) {
2584 var mixinStyles = context.loadedLibrary.styles;
2684 if (mixinStyles) {
2722 var styles = context.libraries.originalConfig.styles || {},
28 configStyles = _.clone(context.config.attributes.styles || styles),
29 assigned = false;
30
3122 ['inline', 'inlineLoader'].forEach(function(key) {
3244 if ((key in mixinStyles) && !(key in styles)) {
336 configStyles[key] = mixinStyles[key];
34
356 assigned = true;
36 }
37 });
38
3922 if (assigned) {
405 context.config.attributes.styles = configStyles;
41 }
42 }
4384 next(complete);
44 },
45
46 outputConfigs: function(context, next, complete) {
47200 if (isInline(context) && context.mode === 'styles') {
48 // Prevent stylesheet output if in inline mode
492 complete(undefined, []);
50 } else {
51198 next(complete);
52 }
53 },
54
55 module: function(context, next, complete) {
56193 next(function(err) {
57193 if (err) {
580 return complete(err);
59 }
60
61193 if (isInline(context)) {
623 context.moduleResources = context.moduleResources.map(function(resource) {
639 if (resource.style || /\.css$/.test(resource.src)) {
643 var generator = function(context, callback) {
653 context.loadResource(resource, function(err, data) {
663 if (err) {
670 return callback(err);
68 }
69
703 var config = context.config,
71 loaderName = config.attributes.styles.inlineLoader || (config.scopedAppModuleName(context.module) + '.loader.loadInlineCSS');
723 callback(err, {
73 data: loaderName + '("'
74 + data.content
75 .replace(/\\/g, '\\')
76 .replace(/\n/g, '\\n')
77 .replace(/"/g, '\\"')
78 + '");\n',
79 inputs: data.inputs,
80 generated: true,
81 noSeparator: true
82 });
83 });
84 };
853 generator.style = true;
863 generator.sourceFile = resource.sourceFile || resource.src;
873 return generator;
88 } else {
896 return resource;
90 }
91 });
92 }
93
94193 complete();
95 });
96 }
97};
98

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/many-to-one-output.js

96%
50
48
2
LineHitsSource
11var _ = require('underscore'),
2 lumbar = require('../lumbar');
3
41function filterDuplicates(context) {
5199 if (context.config.attributes.filterDuplicates === false) {
62 return context.moduleResources;
7 }
8
9197 var paths = {};
10197 return _.filter(context.moduleResources, function(resource) {
11382 if (resource.src) {
12185 var id = (resource.global ? 'global_' : '') + resource.src;
13185 if (paths[id] && !resource.duplicate) {
142 return false;
15 }
16183 paths[id] = true;
17 }
18380 return true;
19 });
20}
21
221function combineResources(context, outputData, callback) {
23169 var resources = context.resources || [];
24169 if (!resources.length) {
2551 return callback();
26 }
27
28118 context.outputFile(function(callback) {
29118 lumbar.combine(
30 context,
31 resources,
32 context.fileName,
33 context.options.minimize && context.mode === 'scripts',
34 context.mode === 'styles',
35 function(err, data) {
36118 data = data || {};
37118 _.extend(data, outputData);
38
39118 if (!data.fileName) {
409 data.fileName = context.fileName;
41 }
42118 if (!data.inputs) {
439 data.inputs = _.chain(resources)
4412 .map(function(resource) { return resource.inputs || resource; })
45 .flatten()
4612 .map(function(resource) { return resource.src || resource; })
4712 .filter(function(resource) { return _.isString(resource); })
48 .map(context.fileUtil.makeRelative, context.fileUtil)
49 .value();
50 }
51
52118 callback(err, data);
53 });
54 },
55 callback);
56}
57
581module.exports = {
59 priority: 1,
60
61 modeComplete: function(context, next, complete) {
62130 next(function(err) {
63130 if (err) {
640 return complete(err);
65 }
66
67130 if (context.combined) {
68 // Build the resources array from each of the modules (Need to maintain proper ordering)
6930 var modules = context.config.moduleList(context.package);
7030 context.resources = [];
7130 modules.forEach(function(module) {
7260 context.resources.push.apply(context.resources, context.combineResources[module]);
73 });
7430 combineResources(context, {}, complete);
75 } else {
76100 complete();
77 }
78 });
79 },
80 module: function(context, next, complete) {
81199 next(function(err) {
82199 if (err) {
830 return complete(err);
84 }
85
86199 if (!context.combined) {
87139 context.resources = filterDuplicates(context);
88139 context.moduleResources = undefined;
89139 combineResources(context, {
90 module: context.module.name
91 },
92 complete);
93 } else {
9460 context.combineResources = context.combineResources || {};
9560 context.combineResources[context.module.name] = filterDuplicates(context);
9660 context.moduleResources = undefined;
9760 complete();
98 }
99 });
100 }
101};
102

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/mixin.js

96%
61
59
2
LineHitsSource
11var _ = require('underscore');
2
31module.exports = {
4 priority: 1,
5
6 loadConfig: function(context, next, complete) {
7138 var modules = context.config.attributes.modules,
8 errored;
9138 _.each(context.libraries.configs, function(library) {
10 // Import any modules that are not overriden in the core file
1179 _.each(library.modules, function(module, key) {
1221 if (!_.has(modules, key)) {
1311 module = modules[key] = _.clone(module);
14
1511 ['scripts', 'styles', 'static', 'routes'].forEach(function(field) {
1644 var value = module[field];
17
18 // Deep(er) clone, updating file references
1944 if (_.isArray(value)) {
2013 module[field] = context.libraries.mapFiles(value, library);
2131 } else if (value) {
220 module[field] = _.clone(value);
23 }
24 });
25 }
26 });
27 });
28
29138 _.each(modules, function(module, name) {
30168 module.name = module.name || name;
31168 var mixins;
32168 try {
33168 mixins = context.libraries.moduleMixins(module);
34 } catch (err) {
352 errored = true;
362 return complete(new Error('Failed mixins for module "' + name + '": ' + err.message));
37 }
38
39 // Map existing files that have mixin references
40166 try {
41166 ['scripts', 'styles', 'static'].forEach(function(field) {
42496 var list = module[field];
43
44496 if (list) {
45126 module[field] = context.libraries.mapFiles(list);
46 }
47 });
48
49165 _.each(mixins, function(mixin) {
5048 var mixinConfig = mixin.mixinConfig,
51 library = mixin.library;
52
53 // Direct copy for any fields that are not already defined on the object.
5448 _.defaults(module, library.attributes);
55
56 // Merge known array/object types
5748 ['scripts', 'styles', 'static', 'routes'].forEach(function(field) {
58192 mergeValues(module, field, library, mixinConfig, context);
59 });
60 });
61 } catch (err) {
621 errored = true;
631 return complete(err);
64 }
65 });
66
67 // Remove suppressed modules completely
68138 _.each(_.keys(modules), function(name) {
69168 if (!modules[name]) {
701 delete modules[name];
71 }
72 });
73
74138 if (!errored) {
75135 next(complete);
76 }
77 }
78};
79
801function firstLocal(collection) {
8158 for (var i = 0, len = collection.length; i < len; i++) {
8273 if (!collection[i].global) {
8358 return i;
84 }
85 }
860 return i;
87}
88
891function mergeValues(module, field, library, mixinConfig, context) {
90192 var value = module[field],
91 mixinValue = library.attributes[field];
92
93192 if (!value) {
94137 return;
95 }
96
9755 if (value === mixinValue) {
98 // Clone any direct copy entries from a mixin
9916 if (_.isArray(value)) {
10014 module[field] = context.libraries.mapFiles(value, library, mixinConfig);
101 } else {
1022 module[field] = _.clone(value);
103 }
10439 } else if (!_.isArray(value)) {
1055 _.defaults(value, mixinValue);
10634 } else if (mixinValue) {
10729 mixinValue = context.libraries.mapFiles(mixinValue, library, mixinConfig);
108
10929 var mixinFirstLocal = firstLocal(mixinValue),
110 moduleFirstLocal = firstLocal(value);
111
11229 if (mixinFirstLocal) {
1134 value.unshift.apply(value, mixinValue.slice(0, mixinFirstLocal));
114 }
11529 if (mixinFirstLocal < mixinValue.length) {
11629 var locals = mixinValue.slice(mixinFirstLocal);
11729 locals.unshift(mixinFirstLocal + moduleFirstLocal, 0);
11829 value.splice.apply(value, locals);
119 }
120 }
121}
122

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/module-map.js

87%
124
108
16
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 handlebars = require('handlebars'),
4 fs = require('fs'),
5 path = require('path'),
6 dirname = path.dirname;
7
81var moduleMapTemplate;
9
101function getModuleMapTemplate() {
1117 if (!moduleMapTemplate) {
121 moduleMapTemplate = handlebars.compile(fs.readFileSync(__dirname + '/module-map.handlebars').toString());
13 }
1417 return moduleMapTemplate;
15}
16
17// Force template load before EMFILE may be an issue
181getModuleMapTemplate();
19
201function loadModuleMap(map, mapper, callback) {
2116 var moduleMapTemplate = getModuleMapTemplate();
22
23 // This bit of voodoo forces uniform ordering for the output under node. This is used primarily for
24 // testing purposes.
2516 map = (function orderObject(map) {
2694 var ret = _.isArray(map) ? [] : {};
2794 _.keys(map).sort().forEach(function(key) {
28167 var value = map[key];
29167 ret[key] = _.isObject(value) ? orderObject(value) : value;
30 });
3194 return ret;
32 })(map);
33
3416 callback(
35 undefined,
36 moduleMapTemplate({
37 moduleMapper: mapper,
38 map: JSON.stringify(map)
39 })
40 );
41}
42
431function buildMap(context, callback) {
4438 if (context.combined) {
4515 moduleConfig(context, undefined, function(err, config, prefix) {
4615 callback(err, { base: config }, prefix);
47 });
48 } else {
4923 var attr = context.config.attributes || {},
50 app = attr.application || {},
51 modules = context.config.moduleList(context.package);
52
5323 var map = {modules: {}, routes: {}},
54 commonPrefix;
55
5623 async.forEach(modules, function(module, callback) {
5732 moduleConfig(context, module, function(err, config, prefix) {
5832 if (err) {
590 return callback(err);
60 }
61
6232 if (app.module === module) {
634 map.base = config;
64 } else {
6528 map.modules[module] = config;
66
6728 var routes = context.config.routeList(module);
6828 _.each(routes, function(value, route) {
6929 map.routes[route] = module;
70 });
71 }
7232 commonPrefix = findPrefix(prefix, commonPrefix);
73
7432 callback();
75 });
76 },
77 function(err) {
7823 callback(err, map, commonPrefix);
79 });
80 }
81}
82
831function stripPrefix(map, prefix) {
8416 if (!prefix) {
8515 return;
86 }
87
881 function stripModule(module) {
890 if (module.js) {
900 module.js = stripList(module.js);
91 }
920 if (module.css) {
930 module.css = stripList(module.css);
94 }
95 }
961 function stripList(list) {
970 if (_.isArray(list)) {
980 return list.map(stripEntry);
99 } else {
1000 return stripEntry(list);
101 }
102 }
1031 function stripEntry(entry) {
1040 if (entry.href) {
1050 entry.href = entry.href.substring(prefix.length);
1060 return entry;
107 } else {
1080 return entry.substring(prefix.length);
109 }
110 }
1111 if (map.base) {
1120 stripModule(map.base);
113 }
1141 if (map.modules) {
1150 _.each(map.modules, stripModule);
116 }
117}
1181function moduleConfig(context, module, callback) {
11947 var ret = {},
120 commonPrefix,
121 preload = module && context.config.module(module).preload,
122 depends = module && context.config.module(module).depends;
12347 if (preload) {
1241 ret.preload = preload;
125 }
12647 if (depends) {
1274 ret.depends = depends;
128 }
12947 async.forEach([{key: 'js', mode: 'scripts'}, {key: 'css', mode: 'styles'}], function(obj, callback) {
13094 fileList(context, obj.mode, module, function(err, list, prefix) {
13194 ret[obj.key] = list;
13294 commonPrefix = findPrefix(prefix, commonPrefix);
13394 callback(err);
134 });
135 },
136 function(err) {
13747 callback(err, ret, commonPrefix);
138 });
139}
1401function fileList(context, mode, module, callback) {
141 // Check to see if we even have this type of resource
14294 var modules = !context.combined ? [ module ] : context.config.moduleList(context.package);
14394 async.some(modules, function(module, callback) {
144124 var resourceContext = context.clone();
145124 resourceContext.mode = mode;
146124 resourceContext.module = context.config.module(module);
147124 resourceContext.isModuleMap = true;
148
149124 resourceContext.plugins.moduleResources(resourceContext, function(err, resources) {
150124 callback((resources || []).length);
151 });
152 },
153 function(hasResource) {
15494 if (!hasResource) {
15515 return callback();
156 }
157
158 // Output the config
15979 context.fileNamesForModule(mode, module, function(err, configs) {
16079 if (err) {
1610 return callback(err);
162 }
163
16479 var prefix;
165184 configs = configs.filter(function(config) { return !config.server; });
166105 configs = configs.sort(function(a, b) { return a.pixelDensity - b.pixelDensity; });
16779 configs = configs.map(function(config, i) {
168105 var path = config.fileName.path,
169 ret = path + '.' + config.fileName.extension;
170
171105 if (config.pixelDensity) {
17261 ret = { href: ret };
17361 if (0 < i) {
17426 ret.minRatio = configs[i - 1].pixelDensity + (config.pixelDensity - configs[i - 1].pixelDensity) / 2;
175 }
17661 if (i < configs.length - 1) {
17726 ret.maxRatio = config.pixelDensity + (configs[i + 1].pixelDensity - config.pixelDensity) / 2;
178 }
179 }
180
181 // Update the prefix tracker
182105 prefix = findPrefix(path, prefix);
183
184105 return ret;
185 });
186
18779 var ret;
18879 if (configs.length === 1) {
18954 ret = configs[0];
19025 } else if (configs.length) {
19125 ret = configs;
192 }
19379 callback(undefined, ret, prefix);
194 });
195 });
196}
197
1981function findPrefix(path, prefix) {
199 /*jshint eqnull:true*/
200231 if (path == null) {
20115 return prefix;
202 }
203216 if (prefix == null) {
204 // Ensure that we get 'x' for strings of type 'x/'
205149 prefix = dirname(path + 'a') + '/';
206 }
207216 for (var i = 0, len = prefix.length; i < len; i++) {
208149 if (path.charAt(i) !== prefix.charAt(i)) {
209149 return prefix.substring(0, i);
210 }
211 }
21267 return prefix;
213}
214
2151module.exports = {
216 mode: 'scripts',
217 priority: 50,
218
219 buildMap: buildMap,
220
221 resource: function(context, next, complete) {
222252 var config = context.config;
223
224252 if (context.resource['module-map']) {
22521 var buildModuleMap = function(context, callback) {
22616 module.exports.buildMap(context, function(err, map, prefix) {
22716 if (err) {
2280 callback(err);
229 } else {
23016 var moduleMap = config.attributes.moduleMap || 'module.exports.moduleMap';
23116 stripPrefix(map, prefix);
23216 loadModuleMap(map, moduleMap, function(err, data) {
23316 callback(err, data && {data: data, generated: true, noSeparator: true, ignoreWarnings: true});
234 });
235 }
236 });
237 };
23821 buildModuleMap.sourceFile = undefined;
23921 complete(undefined, buildModuleMap);
240 } else {
241231 next(complete);
242 }
243 }
244};
245

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/package-config.js

90%
21
19
2
LineHitsSource
11var handlebars = require('handlebars');
2
31const DEFAULT_CONFIG_TEMPLATE = "{{{name}}} = {{{data}}};\n";
41var packageConfigTemplate = handlebars.compile(DEFAULT_CONFIG_TEMPLATE);
5
61function loadPackageConfig(name, configFile, fileUtil, callback) {
716 if (!configFile) {
81 return callback(new Error('package_config.json specified without file being set'));
9 }
10
1115 fileUtil.readFile(configFile, function(err, data) {
1215 if (err) {
130 callback(new Error('Failed to load package config "' + configFile + '"\n\t' + err));
140 return;
15 }
16
1715 callback(
18 undefined,
19 packageConfigTemplate({
20 name: name,
21 data: data
22 })
23 );
24 });
25}
26
271module.exports = {
28 mode: 'scripts',
29 priority: 50,
30
31 resource: function(context, next, complete) {
32290 var resource = context.resource;
33
34290 if (resource['package-config']) {
3517 var packageConfigGen = function(context, callback) {
3616 var config = context.config,
37 options = context.options,
38 packageConfig = config.attributes.packageConfig || 'module.exports.config';
39
4016 loadPackageConfig(packageConfig, options.packageConfigFile, context.fileUtil, function(err, data) {
4116 callback(err, data && {data: data, inputs: [options.packageConfigFile], generated: true, noSeparator: true});
42 });
43 };
4417 packageConfigGen.sourceFile = undefined;
4517 complete(undefined, packageConfigGen);
46 } else {
47273 next(complete);
48 }
49 }
50};
51

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/router.js

95%
22
21
1
LineHitsSource
11var handlebars = require('handlebars');
2
31const TEMPLATE = '/* router : {{{name}}} */\nmodule.name = "{{{name}}}";\nmodule.routes = {{{routes}}};\n';
41var routerTemplate = handlebars.compile(TEMPLATE);
5
61function loadRouter(context, name, routes, callback) {
714 callback(
8 undefined,
9 routerTemplate({
10 name: name,
11 routes: JSON.stringify(routes)
12 })
13 );
14}
15
161module.exports = {
17 mode: 'scripts',
18 priority: 50,
19
20 moduleResources: function(context, next, complete) {
21202 next(function(err, ret) {
22202 if (err) {
230 return complete(err);
24 }
25
26 // Generate the router if we have the info for it
27202 var module = context.module;
28202 if (module.routes) {
2953 ret.unshift({ routes: module.routes });
30 }
31
32202 complete(undefined, ret);
33 });
34 },
35 resource: function(context, next, complete) {
36273 var resource = context.resource,
37 module = context.module.name;
38
39273 if (resource.routes) {
4021 var routerGen = function(context, callback) {
4114 loadRouter(context, module, resource.routes, function(err, data) {
4214 callback(err, data && {data: data, generated: true, noSeparator: true});
43 });
44 };
4521 routerGen.moduleStart = true;
4621 routerGen.sourceFile = undefined;
4721 complete(undefined, routerGen);
48 } else {
49252 next(complete);
50 }
51 }
52};
53

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/scope.js

97%
108
105
3
LineHitsSource
1/**
2 * Scope Plugin : Wrap javascript units in module scopes.
3 *
4 * Config:
5 * root:
6 * scope:
7 * scope: Size of the smallest module scope. May be: 'module', 'resource', 'none'
8 * template: Template used override the default module logic.
9 * This may be an inline handlebars template or a reference to a handlebars file.
10 * Available fields:
11 * scope : Name of the javascript module
12 * isTopNamespace : Truthy if the current module is a top level namespace
13 * appName : Name of the application object
14 * yield : Location that the embedded javascript will be inserted
15 * aliases : Key value mapping of objects that will be imported into the module locally.
16 * This is useful for allowing minimization of commonly used objects such as the
17 * application object or common libraries.
18 *
19 * root.scope may be set to the scope values as a shorthand.
20 *
21 * Mixins:
22 * All fields may be mixed in. Template file references are converted to mixin space. The alias
23 * field will be mixed in per-key with the local definition taking priority.
24 */
251var _ = require('underscore');
26
271function getScope(attr) {
28419 return (attr.scope && attr.scope.scope) || attr.scope;
29}
301function toObj(obj) {
3196 return _.isString(obj) ? {scope: obj} : obj;
32}
33
341function generator(string) {
35166 var ret = function(context, callback) { callback(undefined, {data: string, generated: true, noSeparator: true}); };
3696 ret.stringValue = string;
3796 ret.sourceFile = undefined;
3896 ret.ignoreWarnings = true;
3996 return ret;
40}
41
421var scopeTemplateDelimiter = /\{?\{\{yield\}\}\}?/;
43
441function ensureModuleTemplates(context, complete) {
4547 if (!context.configCache.moduleTemplate) {
4630 var template = context.config.attributes.scope && context.config.attributes.scope.template;
4730 if (!template) {
4818 template = __dirname + '/scope-module.handlebars';
49 }
50
5130 context.fileUtil.loadTemplate(template, scopeTemplateDelimiter, function(err, templates) {
5231 if (err) {
531 complete(err);
54 } else {
5530 context.configCache.moduleTemplate = {
56 start: templates[0],
57 end: templates[1]
58 };
5930 complete();
60 }
61 });
62 } else {
6317 complete();
64 }
65}
66
671function wrapResources(resources, context) {
6846 var cache = context.moduleCache;
6946 if (!cache.scopeName) {
7046 var app = context.config.attributes.application,
71 appName = app && app.name;
72
7346 if (!appName || context.module.topLevelName || context.config.isAppModule(context.module)) {
7432 cache.isTopNamespace = true;
7532 cache.scopeName = context.module.topLevelName || appName || context.module.name;
76 } else {
7714 cache.scopeName = appName + "['" + context.module.name + "']";
78 }
7946 cache.appName = appName;
80 }
81
82 // Wrap the module content in a javascript module
8346 if (resources.length) {
8446 function isModule(reference) {
8526 var stripOperators = /['"\]]/g;
8626 return reference === cache.scopeName
87 || (!cache.isTopNamespace
88 && reference.replace(stripOperators, '').substr(-context.module.name.length) === context.module.name);
89 }
90
9146 var scope = context.config.attributes.scope || {},
92
93 // Call args calculation
94 aliasesHash = context.module.aliases === false ? {} : _.extend({}, scope.aliases, context.module.aliases),
95 aliases = _.pairs(aliasesHash),
9616 aliases = _.filter(aliases, function(alias) { return alias[1]; }),
9713 externals = _.filter(aliases, function(alias) { return !isModule(alias[1]); }),
98 aliasVars = _.pluck(externals, '0'),
99 callSpec = _.pluck(externals, '1'),
100
101 // Internal scope calculation
10213 internals = _.filter(aliases, function(alias) { return isModule(alias[1]); }),
103 internalVars = _.pluck(internals, '0'),
104 internalScope = '';
105
10646 callSpec.unshift('this');
10746 if (cache.isTopNamespace) {
10832 internalVars.unshift(cache.scopeName);
109 } else {
11014 internalScope += cache.scopeName + ' = exports;';
111 }
11284 internalVars = _.map(internalVars, function(name) { return name + ' = exports'; });
11346 if (internalVars.length) {
11433 internalScope += 'var ' + internalVars.join(', ') + ';';
115 }
116
11746 var scopeDecl = '';
11846 if (context.moduleCache.isTopNamespace) {
119 // Insert the package declaration
12032 scopeDecl = 'var ' + context.moduleCache.scopeName + ';';
121 }
12246 var templateContext = {
123 isTopNamespace: cache.isTopNamespace,
124 name: cache.appName,
125 scopeDecl: scopeDecl,
126 scope: cache.scopeName,
127 aliasVars: aliasVars.join(', '),
128 internalScope: internalScope,
129 callSpec: callSpec.join(', ')
130 };
131
13246 resources.unshift(generator(context.configCache.moduleTemplate.start(templateContext)));
13346 resources.push(generator(context.configCache.moduleTemplate.end(templateContext)));
134 }
13546 return resources;
136}
137
1381module.exports = {
139 mode: 'scripts',
140 priority: 50,
141
142 loadMixin: function(context, next, complete) {
14384 var mixinScope = toObj(context.loadedLibrary.scope);
14484 if (mixinScope) {
1456 var scope = toObj(context.libraries.originalConfig.scope || {}),
146 configScope = toObj(_.clone(context.config.attributes.scope || scope)),
147 assigned = false;
148
1496 if (('scope' in mixinScope) && !('scope' in scope)) {
1502 configScope.scope = mixinScope.scope;
151
1522 assigned = true;
153 }
154
1556 if (('template' in mixinScope) && !('template' in scope)) {
1563 configScope.template = (context.loadedLibrary.root || '') + mixinScope.template;
157
1583 assigned = true;
159 }
160
1616 if (context.libraries.mergeHash('aliases', scope, mixinScope, configScope)) {
1623 assigned = true;
163 }
164
1656 if (assigned) {
1664 context.config.attributes.scope = configScope;
167 }
168 }
16984 next(complete);
170 },
171 loadConfig: function(context, next, complete) {
172135 var modules = context.config.attributes.modules;
173
174135 try {
175135 _.each(modules, function(module) {
176162 var mixins = context.libraries.moduleMixins(module);
177
178162 _.each(mixins, function(mixin) {
17948 context.libraries.mergeHash('aliases', module, mixin.library.attributes, module);
180 });
181 });
182 } catch (err) {
1830 return complete(err);
184 }
185
186135 next(complete);
187 },
188
189 resourceList: function(context, next, complete) {
190315 next(function(err, resources) {
191315 if (err) {
1920 return complete(err);
193 }
194
195315 if (getScope(context.config.attributes) === 'resource'
196 && !context.resource.global
197 && !context.resource.dir) {
1982 resources.unshift(generator('(function() {\n'));
1992 resources.push(generator('}).call(this);\n'));
200 }
201315 complete(undefined, resources);
202 });
203 },
204
205 module: function(context, next, complete) {
206104 next(function(err) {
207104 if (err) {
2080 return complete(err);
209 }
210
211104 var resources = context.moduleResources,
212 scope = getScope(context.config.attributes);
213
214104 if (resources.length && scope !== 'none') {
21547 ensureModuleTemplates(context, function(err) {
21648 if (err) {
2171 complete(err);
218 } else {
219 // Split up globals and non-globals
22047 var globals = [],
221 children = [],
222 moduleStart = [];
22347 for (var i = 0; i < resources.length; i++) {
224160 var resource = resources[i];
225160 if (resource.moduleStart) {
22613 moduleStart.push(resource);
227147 } else if (!resource.global) {
228131 children.push(resource);
229 } else {
23016 if (children.length) {
2311 throw new Error('Scoped files may not appear before global files.\n' + _.map(children, function(resource) {
2321 return resource.stringValue || resource.sourceFile || resource.src || resource;
233 }).join(', ') + ', ' + (resource.src || resource));
234 }
23515 globals.push(resource);
236 }
237 }
238
23946 children = moduleStart.concat(children);
24046 globals.push.apply(globals, wrapResources(children, context, complete));
241
24246 context.moduleResources = globals;
24346 complete();
244 }
245 });
246 } else {
24757 complete();
248 }
249 });
250 }
251};
252
253

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/scripts-output.js

100%
2
2
0
LineHitsSource
11var _ = require('underscore'),
2 manyToOne = require('./many-to-one-output');
3
41module.exports = _.extend({ mode: 'scripts' }, manyToOne);
5

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/scripts.js

80%
5
4
1
LineHitsSource
11module.exports = {
2 mode: 'scripts',
3 priority: 99,
4
5 fileFilter: function(context, next) {
60 return /\.(js|json)$/;
7 },
8
9 fileName: function(context, next, complete) {
10113 complete(undefined, {path: context.baseName, extension: 'js'});
11 },
12
13 moduleResources: function(context, next, complete) {
14202 var module = context.module;
15202 complete(undefined, (module.scripts || module.files || (module.slice && module) || []).slice());
16 }
17};
18

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/server-scripts.js

100%
29
29
0
LineHitsSource
11var _ = require('underscore');
2
31module.exports = {
4 mode: 'scripts',
5 priority: 98, // Just below the core scripts plugin....
6
7 fileFilter: function(context, next) {
8141 return /\.(js|json)$/;
9 },
10
11 outputConfigs: function(context, next, complete) {
12108 next(function(err, files) {
13108 if (err) {
141 return complete(err);
15 }
16
17 // Permutation of other configs and ours
18107 var ret = [];
19107 files.forEach(function(fileConfig) {
20109 [true, false].forEach(function(server) {
21 // If they did not opt into server mode then we want to only emit non-server
22 // mode
23218 if (context.config.attributes.server || !server) {
24111 var config = _.clone(fileConfig);
25111 config.server = server;
26111 ret.push(config);
27 }
28 });
29 });
30107 complete(undefined, ret);
31 });
32 },
33
34 fileName: function(context, next, complete) {
35115 next(function(err, ret) {
36115 if (ret && context.fileConfig.server) {
371 ret.path += '-server';
38 }
39115 complete(err, ret);
40 });
41 },
42
43 moduleResources: function(context, next, complete) {
44208 var module = context.module;
45
46208 if (module.server && context.fileConfig.server) {
471 return complete(undefined, module.server);
48 }
49
50207 next(function(err, scripts) {
51207 if (err) {
521 return complete(err);
53 }
54
55206 var files = [];
56206 _.each(scripts, function(script) {
57483 if (!_.has(script, 'server') || script.server === context.fileConfig.server) {
58481 files.push(script);
59 }
60 });
61206 complete(undefined, files);
62 });
63 }
64};
65

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/static-output.js

88%
17
15
2
LineHitsSource
11var async = require('async');
2
31module.exports = {
4 mode: 'static',
5 priority: 1,
6
7 module: function(context, next, complete) {
876 next(function(err) {
976 async.forEach(context.moduleResources, function(resource, callback) {
1028 var fileContext = context.clone();
1128 fileContext.resource = resource;
12
13 // Filter out dir entries
1428 if (resource.dir) {
152 return callback();
16 }
17
1826 fileContext.outputFile(function(callback) {
1926 var fileInfo = fileContext.loadResource(resource, function(err, data) {
2026 if (err || !data || !data.content) {
210 return callback(err);
22 }
23
2426 var ret = {
25 fileName: fileContext.fileName,
26 inputs: fileInfo.inputs || [ fileInfo.name ],
27 module: context.module.name,
28 resource: resource
29 };
30
3126 context.fileUtil.writeFile(fileContext.fileName, data.content, function(err) {
3226 if (err) {
330 err = new Error('Static output "' + fileContext.fileName + '" failed\n\t' + err);
34 }
35
3626 callback(err, ret);
37 });
38 });
39 },
40 callback);
41 },
42 complete);
43 });
44 }
45};
46

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/static.js

92%
25
23
2
LineHitsSource
11var _ = require('underscore');
2
3/*
4 * Replace variables with actual values
5 */
61function replaceVariables(str, context) {
778 return str.replace(/\#\{platform\}/, context.platform);
8}
9
10/*
11 * Make sure the directory name has a trailing slash
12 */
131function normalizeDirName(dirName) {
142 if (dirName.match(/\/$/)) {
150 return dirName;
16 }
172 return dirName + '/';
18}
19
201module.exports = {
21 mode: 'static',
22 priority: 99,
23
24 fileName: function(context, next, complete) {
2526 var resource = context.resource,
26 src = resource.src || resource.sourceFile,
27 dir = resource.dir;
28
2926 var root = '';
30
3126 if (resource.srcDir && resource.dest) {
32 // srcDir is some prefix of src - we want to append the remaining part or src to dest
332 src = src.substring(resource.srcDir.length + 1);
342 root += normalizeDirName(resource.dest);
3524 } else if (resource.dest) {
3619 src = resource.dest;
37 }
38
3926 root = replaceVariables(root, context);
4026 src = replaceVariables(src, context);
41
4226 var components = /(.*?)(?:\.([^.]+))?$/.exec(src);
4326 complete(undefined, {root: resource.root, path: root + components[1], extension: components[2]});
44 },
45
46 resource: function(context, next, complete) {
4728 next(function(err, resource) {
4828 if (_.isString(resource)) {
490 resource = replaceVariables(resource, context);
5028 } else if (resource.src) {
5126 resource.src = replaceVariables(resource.src, context);
52 }
5328 complete(undefined, resource);
54 });
55 }
56};
57

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/styles-output.js

100%
2
2
0
LineHitsSource
11var _ = require('underscore'),
2 manyToOne = require('./many-to-one-output');
3
41module.exports = _.extend({ mode: 'styles' }, manyToOne);
5

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/styles.js

100%
2
2
0
LineHitsSource
11module.exports = {
2 mode: 'styles',
3 priority: 99,
4
5 fileName: function(context, next, complete) {
6118 complete(undefined, {path: context.baseName, extension: 'css'});
7 }
8};
9

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/stylus-config.js

94%
39
37
2
LineHitsSource
11var _ = require('underscore'),
2 async = require('async');
3
41module.exports = {
5 mode: ['scripts', 'styles'],
6 priority: 25,
7
8 loadMixin: function(context, next, complete) {
984 var mixinStyles = context.loadedLibrary.styles;
1084 if (mixinStyles) {
1122 var styles = context.libraries.originalConfig.styles || {},
12 configStyles = _.clone(context.config.attributes.styles || styles),
13 assigned = false;
14
1522 ['configObject'].forEach(function(key) {
1622 if ((key in mixinStyles) && !(key in styles)) {
171 configStyles[key] = mixinStyles[key];
18
191 assigned = true;
20 }
21 });
22
2322 if (context.libraries.mergeFiles('config', styles, mixinStyles, configStyles, context.loadedLibrary)) {
244 assigned = true;
25 }
26
2722 if (assigned) {
284 context.config.attributes.styles = configStyles;
29 }
30 }
3184 next(complete);
32 },
33
34 resource: function(context, next, complete) {
35433 if (context.resource['stylus-config']) {
363 var configGenerator = function(context, callback) {
37 // TODO : Load and output the JSON config options
38 // We can use normal JSON.parse here as stylus uses this -> we can call extend as part of the build
393 var styles = context.config.attributes.styles || {},
40 configFiles = styles.config || [],
41 stylusConfig = styles.configObject || 'module.exports.stylusConfig';
42
439 configFiles = _.map(configFiles, function(config) { return config.src || config; });
44
453 async.map(configFiles,
46 function(config, callback) {
476 context.fileUtil.readFile(config, function(err, data) {
486 callback(err, data);
49 });
50 },
51 function(err, data) {
523 if (data) {
533 try {
543 var config = _.reduce(data, function(config, json) {
556 return _.extend(config, JSON.parse(json));
56 }, {});
573 data = {data: stylusConfig + ' = ' + JSON.stringify(config) + ';\n', inputs: configFiles, noSeparator: true};
58 } catch (parseError) {
59 // TODO : Better error handling here?
600 err = parseError;
610 data = undefined;
62 }
63 }
643 callback(err, data);
65 });
66 };
673 configGenerator.sourceFile = undefined;
683 complete(undefined, configGenerator);
69 } else {
70430 next(complete);
71 }
72 },
73
74 module: function(context, next, complete) {
75199 next(function() {
76199 var styles = context.config.attributes.styles || {},
77 config = styles.config || [];
78
79199 if (config.length) {
8059 _.each(context.moduleResources, function(resource) {
81218 if (resource.stylus) {
8229 resource.plugins.push({
83 plugin: __dirname + '/stylus-config-worker',
84 data: config
85 });
86 }
87 });
88 }
89
90199 complete();
91 });
92 }
93};
94

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/stylus.js

94%
131
124
7
LineHitsSource
1/**
2 * Stylus Plugin : Compile stylus files.
3 *
4 * Config:
5 * root:
6 * styles:
7 * includes: Array of paths to add to stylus includes.
8 * pixelDensity: Defines the pixel densities generated for each plaform.
9 * urlSizeLimit: Maximum file size to inline. Passed to stylus-images plugin
10 * copyFiles: Boolean specifying if non-inlined url references should be compied
11 * To the build directly. Passed to stylus-images plugin.
12 * styleRoot: Project path to resolve files from.
13 * useNib: Truthy to include nib in the project build
14 *
15 * Mixins:
16 * All fields may be mixed in. In the case of conflicts the local config wins for simple values and
17 * for arrays the content will be merged in order. pixelDensity is mixed in at the platform definition
18 * level. File references are converted to mixin space.
19 *
20 * styleRoot is used locally for file lookup when compiling the mixin content.
21 */
221var _ = require('underscore'),
23 ChildPool = require('child-pool'),
24 inlineStyles = require('./inline-styles'),
25 path = require('path'),
26 normalize = path.normalize,
27 fu = require('../fileUtil'),
28 resources = require('../util/resources');
29
30// Forward cache resets to any workers
311fu.on('cache:reset', function(path) {
32259 worker.sendAll({type: 'cache:reset', path: path});
33});
34
351var worker = new ChildPool(__dirname + '/stylus-worker', {logId: 'stylus-worker'});
36
371function generateSource(context, options, styleConfig) {
3838 var includes = (styleConfig.includes || []).concat(options.files),
39 module = options.module;
40
4138 var nibLocation = includes.indexOf('nib'),
42 useNib;
4338 if (styleConfig.useNib) {
4431 useNib = true;
4531 includes.unshift('nib');
467 } else if (nibLocation >= 0) {
47 // Special case nib handling to maintain backwards compatibility
48 // WARN: This may be deprecated in future releases
490 useNib = true;
500 includes.splice(nibLocation, 1);
51 }
52
5338 var declare = context.config.platformList().map(function(platform) {
5473 return '$' + platform + ' = ' + (platform === context.platform);
55 }).join('\n') + '\n';
56
5738 var mixins = [],
58 mixinLUT = {};
59
6038 var source = declare + includes.map(function(include) {
61127 var source = include.library;
62127 var statement = '@import ("' + (include.originalSrc || include.src || include) + '")\n';
63127 if (source) {
6418 var name = '',
65 root = (source.parent || source).root || '',
66 stylusRoot = ((source.parent || source).styles || {}).styleRoot,
67 library = (source.parent || source).name || '';
6818 if (source.parent) {
6915 name = source.name || '';
70 }
7118 var mixinName = name + '_' + library;
72
7318 if (!mixinLUT[mixinName]) {
749 var overrides = resources.calcOverrides(source, function(library, src, ret) {
759 if (library && library.root) {
763 ret.root = normalize(library.root);
77 }
78
799 if (library) {
803 var styles = library.styles || {};
813 ret.stylusRoot = styles.styleRoot;
823 if (ret.styleRoot) {
830 ret.styleRoot = normalize(ret.styleRoot);
84 }
85 }
86 });
879 mixins.push({
88 root: normalize(root),
89 stylusRoot: stylusRoot && normalize(stylusRoot),
90 overrides: overrides
91 });
929 mixinLUT[mixinName] = mixins.length-1;
93 }
9418 mixinName = mixinLUT[mixinName];
95
9618 return 'push-mixin("' + mixinName + '")\n'
97 + statement
98 + 'pop-mixin()\n';
99 } else {
100109 return statement;
101 }
102 }).join('');
103
10438 return {
105 useNib: useNib,
106 source: source,
107 mixins: mixins
108 };
109}
110
1111function compile(options, callback) {
11238 var context = options.context,
113
114 styleConfig = context.config.attributes.styles || {};
115
11638 var loadPrefix = context.config.loadPrefix(),
117 externalPrefix;
11838 if (loadPrefix) {
1199 externalPrefix = loadPrefix + (context.buildPath.indexOf('/') >= 0 ? path.dirname(context.buildPath) + '/' : '');
120 }
121
12238 var imageOptions = {
123 outdir: path.dirname(context.fileName),
124 resolutions: context.modeCache.pixelDensity,
125 limit: styleConfig.urlSizeLimit,
126 copyFiles: styleConfig.copyFiles,
127 externalPrefix: externalPrefix
128 };
129
13038 var source = generateSource(context, options, styleConfig);
131
13238 context.fileUtil.ensureDirs(context.fileName, function(err) {
13338 if (err) {
1340 return callback(err);
135 }
136
13738 worker.send({
138 plugins: options.plugins,
139
140 useNib: source.useNib,
141 imageOptions: imageOptions,
142
143 filename: options.filename,
144 minimize: context.options.minimize,
145
146 source: source.source,
147 mixins: source.mixins,
148
149 lookupPath: context.fileUtil.lookupPath(),
150 styleRoot: styleConfig.styleRoot && context.fileUtil.resolvePath(styleConfig.styleRoot)
151 },
152 callback);
153 });
154}
155
1561module.exports = {
157 // scripts mode is used also to support inline styles
158 mode: ['styles', 'scripts'],
159 priority: 50,
160
161 loadMixin: function(context, next, complete) {
16284 var mixinStyles = context.loadedLibrary.styles;
16384 if (mixinStyles) {
16422 var styles = context.libraries.originalConfig.styles || {},
165 configStyles = _.clone(context.config.attributes.styles || styles),
166 assigned = false;
167
16822 ['urlSizeLimit', 'copyFiles', 'useNib'].forEach(function(key) {
16966 if ((key in mixinStyles) && !(key in styles)) {
1708 configStyles[key] = mixinStyles[key];
171
1728 assigned = true;
173 }
174 });
175
17622 if (context.libraries.mergeFiles('includes', styles, mixinStyles, configStyles, context.loadedLibrary)) {
1776 assigned = true;
178 }
179
18022 if (context.libraries.mergeHash('pixelDensity', styles, mixinStyles, configStyles)) {
1813 assigned = true;
182 }
183
18422 if (assigned) {
18510 context.config.attributes.styles = configStyles;
186 }
187 }
18884 next(complete);
189 },
190
191 outputConfigs: function(context, next, complete) {
192201 if (!inlineStyles.isInline(context) && context.mode !== 'styles') {
193101 return next(complete);
194 }
195
196100 next(function(err, files) {
197100 if (err) {
1980 return complete(err);
199 }
200
201100 var ret = [],
202 styleConfig = context.config.attributes.styles || {},
203 pixelDensity = styleConfig.pixelDensity || {};
204100 if (context.platform) {
20574 pixelDensity = pixelDensity[context.platform] || pixelDensity;
206 }
207100 if (!_.isArray(pixelDensity)) {
20863 pixelDensity = [ 1 ];
209 }
210100 context.modeCache.pixelDensity = pixelDensity;
211
212 // Permutation of other configs and ours
213100 var primary = true;
214100 files.forEach(function(fileConfig) {
215100 pixelDensity.forEach(function(density) {
216138 var config = _.clone(fileConfig);
217138 config.pixelDensity = density;
218138 config.isPrimary = primary;
219138 primary = false;
220138 ret.push(config);
221 });
222 });
223100 complete(undefined, ret);
224 });
225 },
226
227 fileName: function(context, next, complete) {
228235 if (!inlineStyles.isInline(context) && context.mode !== 'styles') {
229107 return next(complete);
230 }
231
232128 next(function(err, ret) {
233128 if (ret && context.fileConfig.pixelDensity !== 1) {
23440 ret.path += '@' + context.fileConfig.pixelDensity + 'x';
235 }
236128 complete(err, ret);
237 });
238 },
239
240 module: function(moduleContext, next, complete) {
241203 next(function(err) {
242 /*jshint eqnull: true */
243203 if (err) {
2440 return complete(err);
245 }
246
247203 function mergeResources(start) {
24856 var generator = function(context, callback) {
24956 function response(data, density) {
25056 if (data) {
25153 return {
252 data: data.data[density || 1],
253 inputs: data.inputs,
254 noSeparator: true
255 };
256 }
257 }
258
25956 var filename = generator.filename;
260
261 // We only want to call stylus once which will generate the css for all of the
262 // resolutions we support on this platform. This ugly bit of code make sure that
263 // we properly handle all of that loading states that can come into play under these
264 // circumstances while still adhering to the output models prescribed by lumbar.
26556 var queue = context.modeCache['stylus_' + filename];
26656 if (_.isArray(queue)) {
267 // We are currently executing
26818 queue.push({density: context.fileConfig.pixelDensity, callback: callback});
26938 } else if (_.isObject(queue)) {
270 // We already have data
2710 callback(undefined, response(queue, context.fileConfig.pixelDensity));
272 } else {
273 // We need to kick of a stylus build
27438 queue = context.modeCache['stylus_' + filename] = [
275 {density: context.fileConfig.pixelDensity, callback: callback}
276 ];
27738 var options = {
278 filename: filename,
279 files: generator.inputs,
280
281 context: context,
282 module: moduleContext.module, // To play nicely with combined mode
283 plugins: generator.plugins
284 };
28538 compile(options, function(err, data) {
28638 if (err) {
2873 data = undefined;
288 }
28938 _.each(queue, function(callback) {
29056 callback.callback(err, response(data, callback.density));
291 });
29238 context.modeCache['stylus_' + filename] = data;
293 });
294 }
295 };
29656 generator.inputs = resources.splice(start, rangeEnd - start + 1);
297157 generator.filename = 'stylus_' + _.map(generator.inputs, function(file) { return file.originalSrc || file.src; }).join(';');
29856 generator.style = true;
29956 generator.stylus = true;
30056 generator.plugins = [];
301
30256 resources.splice(start, 0, generator);
30356 rangeEnd = undefined;
304 }
305
306 // Merge all consequtive stylus files together
307203 var resources = moduleContext.moduleResources,
308 len = resources.length,
309 rangeEnd;
310203 while (len--) {
311426 var resource = resources[len];
312
313426 if (/\.styl$/.test(resource.src)) {
314101 if (!rangeEnd) {
31556 rangeEnd = len;
316 }
317325 } else if (rangeEnd) {
3183 mergeResources(len + 1);
319 }
320 }
321203 if (rangeEnd != null) {
32253 mergeResources(0);
323 }
324203 complete();
325 });
326 }
327};
328

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/template.js

97%
84
82
2
LineHitsSource
1/**
2 * Template Plugin : Includes templates associated with a given file when said file is imported.
3 *
4 * Config:
5 * root:
6 * templates:
7 * Key value hash mapping file names to arrays of templates to include
8 *
9 * Special Values:
10 * auto-include: Key value pair mapping a regular expression key to a series of values
11 * to insert. Matching groups in the regular expression may be replaced using $i notation.
12 *
13 * Example: 'js/views/(.*)\\.js': ['templates/$1.handlebars']
14 *
15 * Mixins:
16 * The template plugin will mixin auto-include mappings per item, giving priority to the local version.
17 * File mappings will be mixed in but are executed within the scope of the mixin only. I.e. foo.js
18 * in the local file will not match file mappings for foo.js in a mixin.
19 *
20 */
211var _ = require('underscore'),
22 build = require('../build'),
23 path = require('path'),
24 resources = require('../util/resources');
25
261module.exports = {
27 mode: 'scripts',
28 priority: 50,
29
30 loadMixin: function(context, next, complete) {
3184 var mixinTemplates = context.loadedLibrary.templates;
3284 if (mixinTemplates) {
3314 var templates = context.libraries.originalConfig.templates || {},
34 configTemplates = _.clone(context.config.attributes.templates || templates),
35 assigned = false;
36
3714 if (context.libraries.mergeHash('auto-include', templates, mixinTemplates, configTemplates)) {
384 assigned = true;
39 }
40
4114 if (assigned) {
424 context.config.attributes.templates = configTemplates;
43 }
44 }
4584 next(complete);
46 },
47
48 resourceList: function(context, next, complete) {
49315 var library = context.resource.library,
50 attr = (library && library.parent || library) || context.config.attributes;
51
52315 next(function(err, ret) {
53315 if (err || !ret) {
540 return complete(err);
55 }
56
57315 function pushTemplates(templates) {
58282 _.each(templates, function(template) {
5948 var src = template.src;
6048 if (!src || (template.library && !template.library.attributes)) {
6134 var templateLibrary = template.library ? context.libraries.getConfig(template.library) : library;
6234 src = mapSrc(template.src || template, templateLibrary, context);
63 }
64
6548 ret.unshift({
66 src: src,
67 name: template.name || template.src || template,
68 library: templateLibrary || template.library || library,
69 template: true
70 });
71 });
72 }
73
74315 var views = attr.templates || {},
75 globalConfig = context.config.attributes.templates || {},
76 resource = context.resource.originalSrc || context.resource.src || context.resource,
77 mixinRoot = (context.resource.library && context.resource.library.root) || '';
78315 if (_.isString(resource) && resource.indexOf(mixinRoot) === 0) {
79241 resource = resource.substring(mixinRoot.length);
80 }
81
82315 var deferComplete;
83315 if (build.filterResource(context.resource, context)) {
84271 pushTemplates(views[resource]);
85
86271 if (globalConfig['auto-include']) {
8712 var config = context.configCache['template-auto-include'];
8812 if (!config) {
896 config = module.exports.generateMappings(globalConfig['auto-include']);
906 context.configCache['template-auto-include'] = config;
91 }
92
9312 var autoIncludes = module.exports.autoIncludes(resource, config, context);
9412 if (autoIncludes.length) {
9511 deferComplete = true;
96
9711 context.fileUtil.fileList(autoIncludes, function(err, autoIncludes) {
9811 if (err) {
990 return complete(err);
100 }
101
10211 var watchDirs = [];
10311 autoIncludes = _.filter(autoIncludes, function(file) {
10417 if (file.enoent) {
1053 watchDirs.push({watch: path.dirname(file.src)});
106 } else {
10714 return true;
108 }
109 });
110
11111 if (autoIncludes.length) {
1129 context.event.emit('log', 'Autoincludes for "' + resource + '" ' + JSON.stringify(_.pluck(autoIncludes, 'src'), undefined, 2));
113 }
114
11511 pushTemplates(autoIncludes);
11611 ret.unshift.apply(ret, watchDirs);
117
11811 complete(undefined, ret);
119 });
120 }
121 }
122 }
123315 if (!deferComplete) {
124304 complete(undefined, ret);
125 }
126 });
127 },
128
129 resource: function(context, next, complete) {
130231 var resource = context.resource;
131
132231 if (resource.watch) {
1332 function generator(buildContext, callback) {
134 // Ensure that the directory actually exists
1352 var path = context.fileUtil.resolvePath(resource.watch);
1362 context.fileUtil.stat(path, function(err, stat) {
137 // Ignore any errors here
1382 var inputs = [];
1392 if (stat && stat.isDirectory()) {
1402 inputs.push(path);
141 }
1422 callback(undefined, {inputs: inputs, data: '', noSeparator: true});
143 });
144 }
1452 complete(undefined, generator);
146 } else {
147229 next(complete);
148 }
149 },
150
151 autoIncludes: function(resource, config, context) {
15212 var autoIncludes = [];
15312 _.each(config, function(mapping) {
15412 var remap = module.exports.remapFile(mapping, resource, context);
15512 if (remap) {
15611 autoIncludes.push.apply(autoIncludes, remap);
157 }
158 });
15912 return autoIncludes;
160 },
161 generateMappings: function(autoInclude) {
1626 return _.map(autoInclude, function(templates, source) {
1636 if (!_.isArray(templates)) {
1642 templates = [templates];
165 }
1666 return {regex: new RegExp(source), templates: templates};
167 });
168 },
169 remapFile: function(mapping, resource, context) {
170 /*jshint boss:true */
17115 var match;
17215 if (match = mapping.regex.exec(resource)) {
17313 return _.map(mapping.templates, function(template) {
174 // Work in reverse so $10 takes priority over $1
17525 var i = match.length;
17625 while (i--) {
17750 template = template.replace('$' + i, match[i]);
178 }
17925 var resource = context.libraries.mapFile(template, template.library || context.resource.library);
18025 resource = resources.cast(resource);
18125 resource.name = template;
18225 return resource;
183 });
184 }
185 }
186};
187
1881function mapSrc(template, library, context) {
18934 var resource = context.libraries.mapFile(template, library);
19034 return _.isString(resource.src) ? resource.src : resource;
191}
192

/Users/kpdecker/dev/walmart/lumbar/lib/plugins/update-externals.js

92%
57
53
4
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 cheerio = require('cheerio'),
4 path = require('path'),
5 basename = path.basename,
6 dirname = path.dirname;
7
81module.exports = {
9 mode: 'static',
10 priority: 50,
11
12 updateHtmlReferences: function(context, content, callback) {
1315 function updateResources(mode, query, create) {
1430 return function(callback) {
1529 async.forEach($(query), function(el, callback) {
1610 el = $(el);
1710 var module = (el.attr('src') || el.attr('href')).replace(/^module:/, '');
1810 context.fileNamesForModule(mode, module, function(err, fileNames) {
1910 if (err) {
202 return callback(err);
21 }
22
23 // Generate replacement elements for each of the entries
248 var content = fileNames.map(function(fileName) {
258 if (fileName.server) {
260 return '';
27 }
288 return create(loadDirName + basename(fileName.fileName.path) + '.' + fileName.fileName.extension);
29 });
30
31 // Output and kill the original
328 el.replaceWith(content.join(''));
33
348 callback();
35 });
36 },
37 callback);
38 };
39 }
4015 var $ = cheerio.load(content),
41 loadDirName = '';
4215 async.series([
43 function(callback) {
44 // Output the load prefix script we we have a module: script reference
4515 var firstScript = $('script[src^="module:"]');
4615 if (firstScript) {
4715 context.plugins.get('module-map').buildMap(context, function(err, map, loadPrefix) {
4815 if (err) {
490 return callback(err);
50 }
51
5215 var noFileComponent = !loadPrefix;
5315 loadPrefix = context.platformPath + loadPrefix;
5415 var dirname = path.dirname(loadPrefix + 'a'); // Force a component for the trailing '/' case
55
56 // Only remap load prefix if not defined by the user
5715 if (!(loadDirName = context.config.loadPrefix())) {
5814 var resourcePath = path.dirname(context.fileName.substring(context.outdir.length + 1));
5914 loadPrefix = path.relative(resourcePath, loadPrefix);
6014 loadDirName = path.relative(resourcePath, dirname);
61
6214 if (loadDirName) {
637 loadDirName += '/';
64 }
6514 if (loadPrefix && noFileComponent) {
667 loadPrefix += '/';
67 }
68 } else {
69 // A load prefix was given, just combine this with the module map prefix
701 loadPrefix = loadDirName + loadPrefix;
711 if (dirname !== '.') {
721 loadDirName += dirname + '/';
73 }
74 }
75
7615 var script = '<script type="text/javascript">var lumbarLoadPrefix = \'' + loadPrefix + '\';</script>';
7715 firstScript.before(script);
7815 callback();
79 });
80 } else {
810 callback();
82 }
83 },
84 updateResources('scripts', 'script[src^="module:"]', function(href) {
857 return '<script type="text/javascript" src="' + href + '"></script>';
86 }),
87 updateResources('styles', 'link[href^="module:"]', function(href) {
881 return '<link rel="stylesheet" type="text/css" href="' + href + '"/>';
89 })
90 ],
91 function(err) {
9215 callback(err, $.html());
93 });
94 },
95
96 resource: function(context, next, complete) {
9728 var resource = context.resource;
9828 if (resource['update-externals'] || (/\.html?$/.test(resource.src) && resource['update-externals'] !== false)) {
996 next(function(err, resource) {
1006 function generator(context, callback) {
101 // Load the source data
1026 context.loadResource(resource, function(err, file) {
1036 if (err) {
1040 return callback(err);
105 }
106
107 // Update the content
1086 module.exports.updateHtmlReferences(context, file.content, function(err, data) {
1096 callback(err, {
110 data: data,
111 inputs: file.inputs
112 });
113 });
114 });
115 }
116
117 // Include any attributes that may have been defined on the base entry
1186 if (!_.isString(resource)) {
1196 _.extend(generator, resource);
120 }
1216 complete(undefined, generator);
122 });
123 } else {
12422 next(complete);
125 }
126 }
127};
128

/Users/kpdecker/dev/walmart/lumbar/lib/state-machine.js

97%
143
140
3
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 build = require('./build'),
4 combine = require('./jsCombine'),
5 configLoader = require('./config'),
6 Context = require('./context'),
7 fs = require('fs'),
8 fu = require('./fileUtil'),
9 Libraries = require('./libraries'),
10 plugin = require('./plugin'),
11 WatchManager = require('./watch-manager');
12
131exports.loadAndInitDir = function(path, event, options, callback) {
1431 exports.loadConfig(path, event, options, function(err, context) {
1531 if (err) {
160 return callback(err);
17 }
18
1931 exports.ensureDirs(context, function(err) {
2031 return callback(err, context);
21 });
22 });
23};
24
251exports.loadConfig = function(path, event, options, callback) {
2641 try {
2741 fu.resetCache();
28
2941 var config = _.isString(path) ? configLoader.load(path) : configLoader.create(path);
30
3141 var plugins = plugin.create(options);
3241 plugins.initialize(config);
33
3440 config.outdir = options.outdir = options.outdir || config.attributes.output;
35
3640 var libraries = new Libraries(options);
3740 var context = new Context(options, config, plugins, libraries, event);
3840 context.options = options;
3940 context.configCache = {};
40
4140 libraries.initialize(context, function(err) {
4240 if (err) {
431 return callback(err);
44 }
45
4639 plugins.loadConfig(context, function(err) {
4739 if (err) {
481 return callback(err);
49 }
50
5138 event.emit('config', context.config);
5238 if (options.verbose) {
531 event.emit('log', 'Finalized config ' + JSON.stringify(context.config.serialize(), undefined, 2));
54 }
55
5638 callback(undefined, context);
57 });
58 });
59 } catch (err) {
601 callback(err);
61 }
62};
63
641exports.ensureDirs = function(context, callback) {
6531 var config = context.config;
66
67 // Ensure that we have the proper build output
6831 if (!config.outdir) {
691 return callback(new Error('Output must be defined on the command line or config file.'));
70 }
7130 context.outdir = config.outdir;
72
7330 fu.ensureDirs(config.outdir + '/.', function() {
7430 var stat = fs.statSync(config.outdir);
7530 if (!stat.isDirectory()) {
761 callback(new Error('Output must be a directory'));
77 } else {
7829 callback();
79 }
80 });
81};
82
831exports.buildPackages = function(rootContext, packageName, modules, callback) {
8433 if (!callback) {
8511 callback = modules;
8611 modules = undefined;
87 }
88
8933 exports.loadPackages(rootContext, packageName, modules, function(err, contexts) {
9033 if (err) {
910 return callback(err);
92 }
93
9433 exports.buildContexts(contexts, callback);
95 });
96};
97
981exports.loadPackages = function(rootContext, packageName, modules, callback) {
9942 if (!callback) {
1006 callback = modules;
1016 modules = undefined;
102 }
103
104 // Allow a string or a list as modules input
10542 if (!_.isArray(modules)) {
10640 modules = [modules];
1072 } else if (!modules.length) {
108 // Special case empty array input to build all
1091 modules = [undefined];
110 }
111
11242 var options = {};
11342 if (typeof packageName === 'object') {
1141 options = packageName;
1151 packageName = options.package;
116 }
117
11842 var packages = rootContext.config.packageList();
11942 if (packageName && !_.contains(packages, packageName)) {
1201 return callback(undefined, {});
121 }
122
12341 var packageNames = packageName ? [packageName] : packages,
124 contexts = [];
125
12641 packageNames.forEach(function(pkg) {
12746 modules.forEach(function(module) {
12847 options.package = pkg;
12947 options.module = module || undefined; // '' -> undefined
130
13147 rootContext.event.emit('debug', 'Load package: ' + pkg);
132
13347 var platforms = rootContext.config.platformList(pkg);
13447 platforms.forEach(function(platform) {
13564 options.platform = platform;
136
13764 var newContext = rootContext.clone(options);
13864 contexts.push(newContext);
139 });
140 });
141 });
142
14341 var ret = {};
14441 async.forEach(
145 contexts,
146 function(context, callback) {
14764 exports.loadPlatform(context, function(err, contexts) {
14851 if (!err) {
14951 var pkg = ret[context.package] = ret[context.package] || {};
15051 pkg[context.platform] = _.flatten(contexts);
151 }
15251 return callback(err);
153 });
154 },
155 function(err) {
15635 callback(err, ret);
157 });
158};
159
1601exports.loadPlatform = function(context, callback) {
16188 context.event.emit('debug', 'Load platform: ' + context.description);
16288 var modes = context.mode ? [context.mode] : context.plugins.modes();
163
16488 async.map(modes, function(mode, callback) {
165184 exports.loadMode(mode, context, callback);
166 },
167 function(err, contexts) {
16886 callback(err, contexts && _.flatten(contexts));
169 });
170};
171
1721exports.loadMode = function(mode, context, callback) {
173184 context.event.emit('debug', 'Load mode: ' + context.description);
174
175184 context = context.clone();
176184 context.mode = mode;
177184 context.modeCache = {};
178
179184 if (context.fileConfig) {
18021 callback(undefined, [processFileConfig(context.fileConfig)]);
181 } else {
182163 context.plugins.outputConfigs(context, function(err, configs) {
183163 if (err) {
1841 return callback(err);
185 }
186
187162 callback(undefined, _.map(configs, processFileConfig));
188 });
189 }
190
191184 function processFileConfig(fileConfig) {
192194 var fileContext = context.clone(true);
193194 fileContext.fileConfig = fileConfig;
194
195194 return fileContext;
196 }
197};
198
1991exports.buildContexts = function(configContexts, callback) {
20073 if (configContexts instanceof Context) {
2010 configContexts = [configContexts];
20273 } else if (!_.isArray(configContexts)) {
20333 configContexts = _.map(configContexts, function(package) {
20436 return _.values(package);
205 });
20633 configContexts = _.flatten(configContexts);
207 }
208
20973 async.forEach(
210 configContexts,
211 function(fileContext, callback) {
212193 var modules = fileContext.module ? [fileContext.module] : fileContext.config.moduleList(fileContext.package);
213
214193 fileContext.resources = [];
215193 fileContext.combineResources = {};
216193 fileContext.fileCache = fileContext.combined ? {} : undefined;
217
218193 async.forEach(modules, function(module, callback) {
219281 var moduleContext = fileContext.clone();
220281 moduleContext.module = module;
221
222281 exports.buildModule(moduleContext, callback);
223 },
224 function(err) {
225189 if (err) {
2264 return callback(err);
227 }
228
229185 fileContext.plugins.modeComplete(fileContext, callback);
230 });
231 },
232 callback);
233};
234
2351exports.buildModule = function(context, callback) {
236275 context.event.emit('debug', 'Build module: ' + context.description);
237
238275 var module = context.config.module(context.module);
239275 if (!module) {
2401 return callback(new Error('Unable to find module "' + context.module + '"'));
241 }
242
243274 context.module = module;
244274 context.moduleCache = {};
245274 context.fileCache = context.combined ? context.fileCache : {};
246
247274 var resource = context.resource;
248274 if (resource) {
2497 resource = resource.originalResource || resource;
2507 exports.processResources(context, [resource], callback);
251 } else {
252 // Load all resources associated with this module
253267 build.loadResources(context, function(err, resources) {
254267 if (err) {
2551 return callback(err);
256 }
257266 exports.processResources(context, resources, callback);
258 });
259 }
260};
261
2621exports.processResources = function(context, resources, callback) {
263273 build.processResources(resources, context, function(err, resources) {
264273 if (err) {
2651 return callback(err);
266 }
267
268272 context.moduleResources = resources;
269272 context.plugins.module(context, callback);
270 });
271};
272

/Users/kpdecker/dev/walmart/lumbar/lib/templateUtil.js

100%
4
4
0
LineHitsSource
11const ESCAPER_LUT = {
2 '\b': '\\b',
3 '\f': '\\f',
4 '\n': '\\n',
5 '\r': '\\r',
6 '\t': '\\t',
7 '\v': '\\v',
8 '\'': '\\\'',
9 '\"': '\\\"',
10 '\\': '\\\\'
11};
121const ESCAPER = /[\b\f\n\r\t\v\'\"\\]/g;
13
141exports.escapeJsString = function(string) {
15 // TODO : Handle unicode escapes
1669 return string.replace(ESCAPER, function(c) { return ESCAPER_LUT[c] || c; });
17};
18

/Users/kpdecker/dev/walmart/lumbar/lib/util/file-map.js

95%
71
68
3
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 fu = require('../fileUtil'),
4 path = require('path'),
5 dirname = path.dirname,
6 basename = path.basename,
7 sourceMap,
8 SourceMapConsumer,
9 SourceMapGenerator;
10
111try {
121 sourceMap = require('source-map');
131 SourceMapConsumer = sourceMap.SourceMapConsumer;
141 SourceMapGenerator = sourceMap.SourceMapGenerator;
15} catch (err) {
16 /* NOP */
17}
18
191const WARNING_CONTEXT = 3,
20 GENERATED = '<generated';
21
221module.exports = exports = function(output) {
23121 this.output = output;
24121 if (SourceMapGenerator) {
25121 this.generator = new SourceMapGenerator({file: output});
26 }
27
28121 this.contentCache = {};
29121 this.line = 1;
30121 this.column = 1;
31121 this._content = '';
32};
33
341exports.prototype.add = function(name, content, context, generated) {
35511 this._sourceMap = '';
36511 this._consumer = undefined;
37
38511 var lines = content.split('\n');
39511 if (name && !generated) {
40191 this.contentCache[name] = {
41 lines: lines,
42 context: context
43 };
44 }
45
46511 if (this.generator) {
47511 _.each(lines, function(line, index) {
482822 this.generator.addMapping({
49 source: generated && name ? (GENERATED + ':' + name + '>') : (name || GENERATED + '>'),
50 generated: {
51 line: this.line + index,
52 column: index ? 1 : this.column
53 },
54 original: {
55 line: index + 1,
56 column: 1
57 }
58 });
59 }, this);
60 }
61
62511 this.line += lines.length - 1;
63511 if (lines.length >= 2) {
64480 this.column = 1;
65 }
66511 this.column += lines[lines.length - 1].length;
67
68511 this._content += content;
69};
701exports.prototype.content = function() {
71275 return this._content;
72};
731exports.prototype.sourceMap = function() {
744 this._sourceMap = this._sourceMap || this.generator.toString();
754 return this._sourceMap;
76};
77
781exports.prototype.sourceMapToken = function() {
793 return '//@ sourceMappingURL=' + basename(this.output) + '.map\n';
80};
81
821exports.prototype.writeSourceMap = function(options) {
833 var tasks = [],
84 outputDir = dirname(this.output) + '/',
85 self = this;
86
873 tasks.push(function(callback) {
883 fu.writeFile((options.mapDestination || self.output) + '.map', self.sourceMap(), callback);
89 });
903 if (options.outputSource) {
911 _.each(this.contentCache, function(content, name) {
922 tasks.push(function(callback) {
932 var file = outputDir + name;
942 fu.ensureDirs(dirname(file), function(err) {
952 if (err) {
960 return callback(err);
97 }
982 fu.writeFile(file, content.lines.join('\n'), callback);
99 });
100 });
101 });
102 }
103
1043 async.parallel(tasks, function(err) {
1053 if (err) {
1060 throw err;
107 }
108
1093 self.add(undefined, self.sourceMapToken());
1103 options.callback();
111 });
112};
113
1141exports.prototype.context = function(line, column) {
1155 if (!SourceMapConsumer) {
1160 return {
117 file: this.output,
118 line: line,
119 column: column
120 };
121 }
122
1235 this._consumer = this._consumer || new SourceMapConsumer(this.sourceMap());
1245 var original = this._consumer.originalPositionFor({line: line, column: column}),
125 lines;
126
1275 var content = this.contentCache[original.source];
1285 if (content) {
1294 var lines = content.lines,
130 line = original.line - 1,
131 start = Math.max(line - WARNING_CONTEXT + 1, 0),
132 end = Math.min(line + WARNING_CONTEXT, lines.length),
133 gutterWidth = (end + '').length;
1344 line = line + 1;
135
1364 lines = lines.slice(start, end).map(function(value, index) {
13715 var lineNum = start + index + 1,
138 lineText = lineNum + '',
139 buffer = '';
14015 for (var i = lineText.length; i < gutterWidth; i++) {
1417 buffer += ' ';
142 }
14315 buffer += lineText;
14415 buffer += (lineNum === line) ? ': ' : ' ';
14515 buffer += value;
14615 return buffer;
147 });
148 } else {
1491 return;
150 }
151
1524 return {
153 file: original.source,
154 fileContext: content.context,
155 line: original.line,
156 column: original.column,
157 context: lines
158 };
159};
160

/Users/kpdecker/dev/walmart/lumbar/lib/util/resources.js

98%
62
61
1
LineHitsSource
11var _ = require('underscore'),
2 bower = require('bower'),
3 path = require('path'),
4 normalize = path.normalize;
5
6/**
7 * Standalone helpers for resource lifetime management and mapping.
8 */
91var resources = module.exports = {
10 cast: function(resource) {
11823 if (_.isString(resource)) {
12268 return {src: resource};
13 } else {
14555 return resource;
15 }
16 },
17
18 source: function(resource) {
19687 return resource.src || resource.dir || resource;
20 },
21
22 map: function(resource, library, config) {
23330 var bowerPath;
24330 if (_.isString(resource.bower)) {
252 bowerPath = path.join(bower.config.directory, resource.bower);
26 }
27
28 // If no mixin was defined on either side then return the identity
29330 if (!library && !bowerPath) {
30225 return resource;
31 }
32
33105 resource = resources.cast(_.clone(resource));
34
35105 var src = resources.source(resource);
36
37 // Include any config information such as env or platform that may have been
38 // specified on the library settings
39105 _.extend(resource, _.omit(config, 'overrideLibrary'));
40
41105 if (_.isString(src)) {
42103 var override = findOverride(library, src),
43 librarySrc = bowerPath || library.root || '';
44103 librarySrc = librarySrc ? path.join(librarySrc, src) : src;
45
46103 if (override) {
4718 resource.originalSrc = librarySrc;
4818 librarySrc = _.isString(override.override) ? override.override : src;
49
5018 if (override.root) {
516 librarySrc = path.join(override.root, librarySrc);
52 }
5385 } else if (override === false) {
543 return;
55 }
56
57100 if (resource.src) {
5899 resource.src = librarySrc;
591 } else if (resource.dir) {
601 resource.dir = librarySrc;
61 }
62 }
63
64102 resource.library = library;
65102 return resource;
66 },
67
68 calcOverrides: function(library, extend) {
699 var ret = {};
709 while (library) {
7111 _.each(library.overrides, function(override, src) {
72 /*jshint eqnull:true */
739 if (override != null) {
749 ret[src] = {
75 override: override
76 };
779 extend && extend(library.overrideLibrary, src, ret[src]);
78 }
79 });
80
8111 library = library.overrideLibrary;
82 }
839 return ret;
84 },
85
86 relativePath: function(src, library) {
8739 if (src.indexOf('./') === 0) {
880 src = src.substring(2);
89 }
9039 src = normalize(src);
91
92 // Attempt to strip either the root of the base or overriding library as we don't know
93 // which we might be
9439 while (library) {
954 var mixinRoot = library.root || '';
964 if (src.indexOf(mixinRoot) === 0) {
973 return src.substring(mixinRoot.length);
98 }
99
1001 library = library.overrideLibrary;
101 }
102
10336 return src;
104 },
105
106 pathToLibrary: function(src, library) {
10739 src = resources.relativePath(src, library);
108
10939 var overrides = library && library.overrides;
11039 if (overrides) {
1111 overrides = _.invert(overrides);
112
113 // Warn not supporting directories at this point in time. Matches must be 1 to 1
1141 return overrides[src] || src;
115 }
116
11738 return src;
118 }
119};
120
1211function findOverride(library, src) {
122 /*jshint eqnull:true */
123103 var ret;
124103 while (library) {
125123 var override = library.overrides && library.overrides[src];
126123 if (override != null) {
12723 ret = {
128 override: override,
129 root: (library.overrideLibrary || {}).root || ''
130 };
131 }
132
133123 library = library.overrideLibrary;
134 }
135
136103 if (ret) {
13721 return ret.override === false ? false : ret;
138 }
139}
140

/Users/kpdecker/dev/walmart/lumbar/lib/util/watcher.js

90%
101
91
10
LineHitsSource
1/**
2 * Adds dependency watching to the core fs.watchFile implementation.
3 */
41var _ = require('underscore'),
5 fs = require('fs');
6
71var watchedFiles = {};
8
91function notifyWatch(filename, type, sourceChange, trigger) {
1081 var watchInfo = watchedFiles[filename];
1181 if (watchInfo) {
1278 var inQueue = _.find(watchInfo.queue, function(entry) {
130 return entry.type === type
14 && entry.filename === filename
15 && entry.sourceChange === sourceChange;
16 });
17
1878 if (!inQueue) {
1978 var entry = {type: type, filename: filename, sourceChange: sourceChange};
2078 watchInfo.queue.push(entry);
21
2278 function exec() {
2378 watchInfo.queue = _.without(watchInfo.queue, entry);
24
2578 if (watchInfo.callback) {
2652 watchInfo.callback(type, filename, sourceChange);
27 }
2878 watchInfo.parents.forEach(function(parent) {
2943 notifyWatch(parent, type, sourceChange, trigger);
30 });
31 }
32
3378 if (trigger) {
3472 exec();
35 } else {
36 // Debounce so we don't output multiple instances of the same event on platforms
37 // such as linux that may send multiple events on write, etc.
386 _.defer(exec, 200);
39 }
40 }
41 }
42}
43
441function watchFile(filename, callback, parent) {
45185 var watchInfo = {
46 callback: callback,
47 parents: [],
48 queue: []
49 };
50185 if (parent) {
51106 watchInfo.parents.push(parent);
52 }
53185 watchedFiles[filename.virtual || filename] = watchInfo;
54
55185 if (!filename.virtual) {
566 var hasRetried;
57
586 (function watch(ignoreError) {
597 try {
607 var oldStat = fs.statSync(filename),
61 lastType,
62 rewatch;
63
647 var changeHandler = _.debounce(function() {
653 if (rewatch) {
66 // Attempt to reattach on rename
671 watchInfo.watch.close();
681 watch(true);
69 }
703 notifyWatch(filename, lastType, filename);
71 }, 1000);
72
737 watchInfo.watch = fs.watch(filename, function(type) {
748 try {
758 var newStat = fs.statSync(filename);
766 if (newStat.isDirectory()) {
771 notifyWatch(filename, 'create', filename);
785 } else if (newStat.size !== oldStat.size || newStat.mtime.getTime() > oldStat.mtime.getTime()) {
795 oldStat = newStat;
805 if (type === 'rename') {
811 rewatch = true;
82 }
835 lastType = type;
845 changeHandler();
85 }
86 } catch (err) {
872 if (err.code === 'ENOENT') {
88 // The file was removed by the time we got to it. This could be a case of it actually being removed
89 // or a race condtion with rewriting APIs.
902 watchInfo.watch.close();
91
92 // Pause a bit to see if this was a replace that we raced with...
932 setTimeout(function() {
942 try {
952 fs.statSync(filename); // No exception: file still exists, notify and restart the watch
960 notifyWatch(filename, 'change', filename);
970 watch(true);
98 } catch (err) {
99 // The file is really gone... or we just got hit with a race condtion twice. Give up.
1002 notifyWatch(filename, 'remove', filename);
101 }
102 }, 500);
103 } else {
1040 throw err;
105 }
106 }
107 });
108 } catch (err) {
1090 if (!hasRetried && err.code === 'EMFILE') {
1100 hasRetried = true;
1110 setTimeout(function() {
1120 watch(ignoreError);
113 }, 250);
1140 } else if (!ignoreError) {
1150 throw err;
116 }
117 }
118 })();
119 }
120}
121
1221exports.watchFile = function(filename, dependencies, callback) {
123102 var watch = watchedFiles[filename.virtual || filename];
124102 if (!watch) {
125 // Create a watch on this and all others
12679 watchFile(filename, callback);
127 } else {
12823 watch.callback = callback;
129 }
130
131102 filename = filename.virtual || filename;
132
133102 dependencies.forEach(function(depend) {
134279 var watch = watchedFiles[depend.virtual || depend];
135279 if (!watch) {
136106 watchFile(depend, undefined, filename);
137 } else {
138173 if (!_.contains(watch.parents, filename)) {
13990 watch.parents.push(filename);
140 }
141 }
142 });
143};
144
1451exports.trigger = function(type, filename) {
14632 notifyWatch(filename, type, filename, true);
147};
148
1491exports.unwatch = function(filename, dependencies) {
1509 var watch = watchedFiles[filename.virtual || filename];
1519 if (!watch) {
1521 return;
153 }
154
155 // Remove the callback
1568 if (!dependencies) {
1574 watch.callback = undefined;
158 }
159
160 // For each dependency remove the parent link
1618 filename = filename.virtual || filename;
162
1638 _.each(dependencies || watchedFiles, function(depend) {
16418 var watch = watchedFiles[depend.virtual || depend];
16518 if (!watch) {
16614 return;
167 }
168
1694 watch.parents = _.without(watch.parents, filename);
170 });
171
172 // Kill this watch if it can't trigger or fire
1738 var canTrigger = watch.watch || _.some(watchedFiles, function(watch) {
17429 return _.contains(watch.parents, filename);
175 });
1768 if (!watch.callback || !canTrigger) {
1775 unwatch(filename);
178 }
179
180 // Kill any other watches that might not be valid anymore
1818 _.each(_.clone(watchedFiles), function(watch, name) {
18224 if (!watch.callback && !watch.parents.length) {
1834 exports.unwatch(name);
184 }
185 });
186};
1871exports.unwatchAll = function() {
18860 _.each(watchedFiles, function(watch, name) {
189180 unwatch(name);
190 });
191};
192
1931function unwatch(name) {
194185 watchedFiles[name].callback = undefined;
195185 if (watchedFiles[name].watch) {
1966 watchedFiles[name].watch.close();
197 }
198185 delete watchedFiles[name];
199}
200

/Users/kpdecker/dev/walmart/lumbar/lib/watch-manager.js

95%
42
40
2
LineHitsSource
11var _ = require('underscore'),
2 EventEmitter = require('events').EventEmitter,
3 fu = require('./fileUtil'),
4 path = require('path'),
5 watcher = require('./util/watcher');
6
71function WatchManager() {
829 EventEmitter.call(this);
9
1029 this.reset();
11
1229 this._exec = this.setupExec();
13}
14
151WatchManager.prototype = {
16 configFile: function(path, mixins, callback) {
1718 if (_.isFunction(mixins)) {
180 callback = mixins;
190 mixins = undefined;
20 }
21
2218 var self = this;
2318 watcher.watchFile(path, mixins || [], function() {
244 self.emit('watch-change', {fileName: path, config: true});
25
264 self.pushChange({callback: callback, fileName: path, config: true});
27 });
28 },
29 moduleOutput: function(status, callback) {
3077 var self = this;
31
3277 function theWatcher(type, filename, sourceChange) {
3339 self.emit('watch-change', {fileName: sourceChange, output: status.fileName});
3439 self.pushChange({
35 callback: callback,
36 type: type,
37 fileName: status.fileName,
38 sourceChange: sourceChange
39 });
40 }
41
42349 var input = status.inputs.map(function(input) { return fu.resolvePath(input.dir || input); }),
43 removed = _.difference(this.watching[status.fileName], input);
44
4577 if (removed.length) {
462 watcher.unwatch(status.fileName, removed);
47 }
48
4977 watcher.watchFile({ virtual: status.fileName }, input, theWatcher);
5077 this.watching[status.fileName] = input;
51 },
52
53
54 setupExec: function() {
5511 return _.debounce(_.bind(this.flushQueue, this), 500);
56 },
57 flushQueue: function() {
5824 if (this.queue.length) {
5924 _.each(this.queue, function(change) {
6041 change.callback();
61 });
6224 this.queue = [];
63 }
64 },
65
66 reset: function() {
67 // Cleanup what we can, breaking things along the way
68 // WARN: This prevents concurrent execution within the same process.
6952 watcher.unwatchAll();
70
7152 this.watching = {};
7252 this.queue = [];
73 },
74 pushChange: function(change) {
7560 fu.resetCache(change.sourceChange);
7660 if (change.type === 'remove' && change.sourceChange) {
773 fu.resetCache(path.dirname(change.sourceChange));
78 }
79
8060 if (_.find(this.queue, function(existing) {
8145 return existing.config || (change.fileName && (existing.fileName === change.fileName));
82 })) {
83 // If we have a pending config change or changes to the same file that has not started then
84 // we can ignore subsequent changes
857 return;
86 }
87
8853 if (change.config) {
8910 this.reset();
90 }
91
9253 this.queue.push(change);
9353 this._exec();
94 }
95};
96
971WatchManager.prototype.__proto__ = EventEmitter.prototype;
98
991exports = module.exports = WatchManager;
100
Done, without errors.