Running "mochacov:cov" (mochacov) task Coverage

Coverage

92%
2408
2237
171

/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) {
17334 var plugins = context.plugins;
18
19334 function filterResource(resource) {
20565 resource = resources.cast(resource);
21
22565 if (exports.filterResource(resource, context)) {
23484 return resource;
24 }
25 }
26
27334 plugins.moduleResources(context, function(err, files) {
28334 if (err) {
291 return callback(err);
30 }
31
32333 var fileFilter = plugins.fileFilter(context) || /.*/;
33333 fu.fileList(files, fileFilter, function(err, files) {
34333 if (err) {
351 return callback(err);
36 }
37
38332 async.map(files, function(resource, callback) {
39510 var resourceContext = context.clone();
40510 resourceContext.resource = resource;
41510 plugins.resourceList(resourceContext, callback);
42 },
43 function(err, resources) {
44332 resources = _.flatten(resources);
45332 resources = _.map(resources, filterResource);
46897 resources = _.filter(resources, function(resource) { return resource; });
47332 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) {
57962 function check(value, singular, plural) {
582638 if (typeof singular !== 'undefined') {
59172 return singular.not ? singular.not !== value : singular === value;
602466 } 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 }
662393 return true;
67 }
68
69962 function checkResource(resource) {
70966 return check(context.platform, resource.platform, resource.platforms)
71 && check(context.package, resource.package, resource.packages)
72 && check(!!context.combined, resource.combined);
73 }
74962 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) {
88332 var plugins = context.plugins;
89
90332 async.map(resources, function(resource, callback) {
91457 var resourceContext = context.clone();
92457 resourceContext.resource = resource;
93457 plugins.resource(resourceContext, function(err, newResource) {
94457 if (newResource && newResource !== resource) {
95105 newResource.originalResource = resource;
96 }
97
98457 callback(err, newResource);
99 });
100 },
101 function(err, resources) {
102332 if (err) {
1031 return callback(err);
104 }
105
106787 callback(err, resources.filter(function(resource) { return resource; }));
107 });
108};
109

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

86%
72
62
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) {
1038 try {
1138 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.
1538 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) {
3930 fu.lookupPath('');
40
4130 var config = exports.readConfig(lumbarFile);
4230 fu.lookupPath(path.dirname(lumbarFile));
43
4430 return exports.create(config);
45};
46
471exports.create = function(config) {
48185 var packageList, moduleList;
49
50185 function loadPackageList() {
51185 if (!config.packages) {
52137 config.packages = { web: { name: '' } };
53 }
54
55185 packageList = _.keys(config.packages);
56 }
57185 function loadModuleList() {
58185 if (!config.modules) {
591 throw new Error('No modules object defined: ' + JSON.stringify(config, undefined, 2));
60 }
61184 moduleList = _.keys(config.modules);
62 }
63
64185 loadPackageList();
65185 loadModuleList();
66
67184 return {
68 /** @typedef {Object} The raw lumbar file as a JSON object. */
69 attributes: config,
70 loadPrefix: function() {
7152 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() {
8231 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) {
941923 if (config && config.packages && config.packages[pkg]) {
951621 return config.packages[pkg].combine;
96 }
97302 return false;
98 },
99 platformList: function(pkg) {
100106 if (!pkg) {
10161 return config.platforms || [''];
102 } else {
10345 if (config.packages[pkg]) {
10445 return config.packages[pkg].platforms || this.platformList();
105 }
1060 return this.platformList();
107 }
108 },
109
110 moduleList: function(pkg) {
111252 return (config.packages[pkg] || {}).modules || _.keys(config.modules);
112 },
113
114 module: function(name) {
115512 var ret = config.modules[name];
116512 if (ret) {
117509 ret.name = name;
118 }
119512 return ret;
120 },
121 isTopLevel: function(module) {
12252 var app = config.application || {};
12352 if (!app.name) {
12422 return true;
125 }
126
12730 module = module.name ? module : this.module(module);
12830 return module.topLevelName || this.isAppModule(module);
129 },
130 isAppModule: function(module) {
13180 var app = config.application;
13280 return (app && app.module) === (module.name || module);
133 },
134 scopedAppModuleName: function(module) {
13546 var app = config.application;
13646 if (this.isAppModule(module)) {
1374 return 'module.exports';
138 } else {
13942 var app = config.application;
14042 return app && app.name;
141 }
142 },
143
144 routeList: function(module) {
14524 return config.modules[module].routes;
146 },
147
148 serialize: function() {
1492 function objectClone(object) {
15019 var clone = object;
151
15219 if (object && object.serialize) {
153 // Allow specialized objects to handle themselves
1540 clone = object.serialize();
15519 } else if (_.isArray(object)) {
1561 clone = _.map(object, objectClone);
15718 } else if (_.isObject(object)) {
15812 clone = {};
15912 _.each(object, function(value, name) {
16015 clone[name] = objectClone(value);
161 });
162 }
163
164 // Collapse simple resources
16519 if (clone.src && _.keys(clone).length === 1) {
1660 clone = clone.src;
167 }
168
16919 return clone;
170 }
171
1722 return objectClone(this.attributes);
173 }
174 };
175};
176
177

/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) {
82261 this._package = options.package;
92261 this._platform = options.platform;
102261 this._plugins = plugins;
112261 this.mode = options.mode;
122261 this.module = options.module;
132261 this.fileConfig = options.fileConfig;
142261 this.resource = options.resource;
152261 this.config = config;
162261 this.libraries = libraries || options.libraries;
172261 this.event = event || options.event;
18}
191Context.prototype = {
20 fileUtil: fu,
21
22 clone: function(options) {
232080 var ret = new Context(this, this.config);
242080 ret.parent = this;
252080 var prototype = Object.keys(Context.prototype);
262080 for (var name in this) {
2765882 if (this.hasOwnProperty(name) && prototype.indexOf(name) === -1) {
2838842 ret[name] = this[name];
29 }
30 }
312080 if (options) {
32297 _.extend(ret, options);
33297 ret._package = options.package || this._package;
34297 ret._platform = options.platform || this._platform;
35 }
362080 return ret;
37 },
38
39 fileNamesForModule: function(mode, moduleName, callback) {
4085 var context = this.clone();
4185 context.mode = mode;
4285 context.module = moduleName && this.config.module(moduleName);
4385 if (moduleName && !context.module) {
442 return callback(new Error('Unknown module "' + moduleName + '"'));
45 }
46
4783 this.plugins.outputConfigs(context, function(err, configs) {
4883 if (err) {
490 return callback(err);
50 }
51
5283 async.map(configs, function(config, callback) {
53109 var fileContext = context.clone();
54109 fileContext.fileConfig = config;
55109 fileContext._plugins.fileName(fileContext, function(err, fileName) {
56109 config.fileName = fileName;
57109 callback(err, config);
58 });
59 },
60 callback);
61 });
62 },
63
64 loadResource: function(resource, callback) {
65426 if (!callback) {
66 // if only single param, assume as callback and resource from context
670 resource = this.resource;
680 callback = resource;
69 }
70
71426 var fileInfo = {name: resource.hasOwnProperty('sourceFile') ? resource.sourceFile : resource.src};
72
73426 function loaded(err, data) {
74 /*jshint eqnull: true */
75426 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 }
96422 fileInfo.inputs = data.inputs;
97422 fileInfo.generated = data.generated;
98422 fileInfo.noSeparator = data.noSeparator;
99422 fileInfo.ignoreWarnings = data.ignoreWarnings || resource.ignoreWarnings;
100422 fileInfo.content = data.data != null ? data.data : data;
101
102 // Ensure that we dump off the stack
103422 _.defer(function() {
104422 callback(err, fileInfo);
105 });
106 }
107
108426 if (typeof resource === 'function') {
109207 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
117426 return fileInfo;
118 },
119
120 outputFile: function(writer, callback) {
121146 var context = this;
122146 context.plugins.file(context, function(err) {
123146 if (err) {
1240 return callback(err);
125 }
126
127146 context.plugins.fileName(context, function(err, fileName) {
128146 if (err) {
1290 return callback(err);
130 }
131
132146 context.buildPath = (fileName.root ? '' : context.platformPath) + fileName.path + '.' + fileName.extension;
133146 context.fileName = context.outdir + '/' + context.buildPath;
134146 writer(function(err, data) {
135146 data = _.defaults({
136 fileConfig: context.fileConfig,
137 platform: context.platform,
138 package: context.package,
139 mode: context.mode
140 }, data);
141
142146 if (err) {
1433 fs.unlink(context.fileName, function() { /* NOP To Prevent warning */});
1443 data.error = err;
145 }
146146 context.event.emit('output', data);
147
148146 context.fileCache = undefined;
149146 callback(err, data);
150 });
151 });
152 });
153 },
154
155 get description() {
156560 var ret = 'package:' + this.package + '_platform:' + this.platform;
157560 if (this.mode) {
158350 ret += '_mode:' + this.mode;
159 }
160560 if (this.fileName) {
161118 ret += '_config:' + this.fileName;
162 }
163560 if (this.module) {
164340 ret += '_module:' + (this.module.name || this.module);
165 }
166560 if (this.resource) {
167 // TODO : Anything better for this?
16819 ret += '_resource:' + resources.source(this.resource);
169 }
170560 return ret;
171 },
172
1731994 get plugins() { return this._plugins; },
174
1755859 get package() { return this._package; },
1764281 get platform() { return this._platform; },
177 get platformPath() {
178158 return this.platform ? this.platform + '/' : '';
179 },
180
181 get combined() {
1821923 return this.config.combineModules(this.package);
183 },
184 get baseName() {
185229 if (!this.combined) {
186161 return this.module.name;
187 } else {
18868 return (this.config.attributes.packages[this.package] || {}).name || this.package;
189 }
190 },
191
192 get resources() {
193292 if (this.parent) {
1940 return this.parent.resources;
195 } else {
196292 return this._resources;
197 }
198 },
199 set resources(value) {
200372 if (this.parent) {
201336 delete this.parent;
202 }
203372 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) {
15419 path = exports.resolvePath(path);
16
17419 var cache = fileCache[path];
18419 if (cache) {
19195 if (cache.data) {
20102 callback(undefined, cache);
21 } else {
2293 cache.pending.push(callback);
23 }
24195 return;
25 }
26
27224 cache = fileCache[path] = {
28 pending: [callback],
29 artifacts: {}
30 };
31
32224 exec(path, function _callback(err, data) {
33224 if (err && err.code === 'EMFILE') {
340 setTimeout(exec.bind(this, path, _callback), EMFILE_RETRY);
35 } else {
36224 if (err) {
373 delete fileCache[path];
38 }
39
40224 cache.data = data;
41224 cache.pending.forEach(function(callback) {
42317 callback(err, cache);
43 });
44224 exports.emit('cache:set', path);
45 }
46 });
47}
48
491exports.resetCache = function(filePath) {
50305 filePath = filePath && path.normalize(filePath);
51305 exports.emit('cache:reset', filePath);
52
53305 if (filePath) {
54179 filePath = exports.resolvePath(filePath);
55179 delete fileCache[filePath];
56 } else {
57126 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.
651515 if (lookupPath
66 && (pathName[0] !== '/' && pathName.indexOf(':/') === -1 && pathName.indexOf(':\\') === -1)
67 && pathName.indexOf(lookupPath) !== 0) {
681036 return lookupPath + pathName;
69 } else {
70479 return pathName;
71 }
72};
731exports.makeRelative = function(pathName) {
74591 if (pathName.indexOf(lookupPath) === 0) {
75559 return pathName.substring(lookupPath.length);
76 } else {
7732 return pathName;
78 }
79};
80
811exports.lookupPath = function(pathName) {
82157 if (pathName !== undefined) {
8395 lookupPath = pathName;
8495 if (lookupPath && !/\/$/.test(lookupPath)) {
8539 lookupPath += '/';
86 }
87 }
88157 return lookupPath;
89};
90
911exports.stat = function(file, callback) {
92819 fs.stat(file, function(err, stat) {
93819 if (err && err.code === 'EMFILE') {
940 setTimeout(exports.stat.bind(exports, file, callback), EMFILE_RETRY);
95 } else {
96819 callback(err, stat);
97 }
98 });
99};
100
1011exports.readFileSync = function(file) {
10238 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) {
110128 cacheRead(file, fs.readFile.bind(fs), function(err, cache) {
111128 var artifacts = cache.artifacts;
112128 callback(err, {data: cache.data, artifact: artifacts[name]});
113 });
114};
1151exports.setFileArtifact = function(path, name, artifact) {
11628 path = exports.resolvePath(path);
117
11828 var cache = fileCache[path];
11928 if (cache) {
12028 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) {
131244 var dirname = path.dirname(pathname);
132244 exports.stat(dirname, function(err) {
133244 if (err && err.code === 'ENOENT') {
134 // If we don't exist, check to see if our parent exists before trying to create ourselves
13539 exports.ensureDirs(dirname, function() {
13639 fs.mkdir(dirname, parseInt('0755', 8), function _callback(err) {
13739 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.
14139 callback(err && err.code === 'EEXIST' ? undefined : err);
142 }
143 });
144 });
145 } else {
146205 callback();
147 }
148 });
149};
150
1511exports.writeFile = function(file, data, callback) {
152138 exports.resetCache(file);
153
154138 exports.ensureDirs(file, function(err) {
155138 if (err) {
1560 return callback(err);
157 }
158
159138 fs.writeFile(file, data, 'utf8', function _callback(err) {
160138 if (err && err.code === 'EMFILE') {
1610 setTimeout(fs.writeFile.bind(fs, file, data, 'utf8', _callback), EMFILE_RETRY);
162 } else {
163138 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) {
178883 if (_.isFunction(extension)) {
1795 callback = extension;
1805 extension = /.*/;
181 }
182
183883 if (_.isArray(pathname)) {
184310 var files = pathname;
185310 pathname = '';
186310 if (!files.length) {
187125 return callback(undefined, []);
188 }
189185 return handleFiles(false, undefined, _.uniq(files));
190573 } else if (!dirList) {
191412 if (pathname.src) {
1920 resource = resource || pathname;
1930 pathname = pathname.src;
194 }
195
196412 pathname = exports.resolvePath(pathname);
197 }
198573 if (resource && resource.src) {
199189 resource = _.clone(resource);
200189 delete resource.src;
201 }
202
203573 function handleFiles(dirname, err, files, srcDir) {
204243 if (err) {
2050 return callback(err);
206 }
207
208243 var ret = [],
209 count = 0,
210 expected = files.length,
211 prefix = pathname ? pathname.replace(/\/$/, '') + '/' : '';
212
213243 function complete(files, index) {
214610 count++;
215
216610 ret[index] = files;
217
218610 if (count === expected) {
219242 ret = _.flatten(ret);
220
221242 if (srcDir) {
22257 ret = ret.map(function(file) {
223124 file = resources.cast(file);
224124 file.srcDir = srcDir;
225124 return file;
226 });
227 }
228
229242 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
236242 callback(undefined, ret);
237 }
238 }
239
240243 if (!files.length) {
2411 callback(undefined, []);
242 }
243
244243 files.forEach(function(file, index) {
245610 var fileResource = resource;
246610 if (file.src) {
247189 fileResource = resource || file;
248189 file = file.src;
249421 } else if (_.isObject(file)) {
25066 complete(file, index);
25166 return;
252 }
253
254544 exports.fileList(prefix + file, extension, function(err, files) {
255544 if (err) {
2560 callback(err);
2570 return;
258 }
259
260544 complete(files, index);
261 }, dirname, fileResource, srcDir);
262 });
263 }
264
265573 exports.stat(pathname, function(err, stat) {
266573 if (err) {
26763 if (err.code === 'ENOENT') {
26863 callback(undefined, [ _.extend({src: exports.makeRelative(pathname), enoent: true}, resource) ]);
269 } else {
2700 callback(err);
271 }
27263 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) {
30245 function compile(templateStr, callback) {
30334 try {
30434 if (splitOnDelimiter) {
30521 callback(null, templateStr.split(splitOnDelimiter).map(function(bit) {
30642 return handlebars.compile(bit);
307 }));
308 } else {
30913 callback(null, handlebars.compile(templateStr));
310 }
311 } catch (e) {
3121 callback(e);
313 }
314 }
31545 if (template.match(/\.handlebars$/)) {
31620 exports.readFileArtifact(template, 'template', function(err, data) {
31720 if (err) {
3181 return callback(err);
319 }
320
32119 if (data.artifact) {
32210 callback(undefined, data.artifact);
323 } else {
3249 compile(data.data.toString(), function(err, data) {
3259 if (!err) {
3269 exports.setFileArtifact(template, 'template', data);
327 }
3289 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');
8
91exports.combine = function(context, files, output, minimize, noSeparator, callback) {
10
11114 function outputIfCompleted() {
12378 if (completed >= files.length) {
13111 var lastEl,
14 map = new FileMap(output),
15 warnings = [],
16
17 tasks = [];
18
19111 _.each(content, function(el) {
20378 var content = el.content.toString();
21
22378 if (!noSeparator && (!lastEl || !lastEl.noSeparator) && map.content()) {
23114 map.add(undefined, '\n;;\n');
24 }
25
26378 map.add(el.name, content, el, el.generated);
27
28378 lastEl = el;
29 }, '');
30
31111 var inputs = [];
32111 content.forEach(function(el) {
33378 if (el.inputs) {
34113 inputs.push.apply(inputs, el.inputs);
35265 } else if (el.name) {
36178 inputs.push(el.name);
37 }
38 });
39111 inputs = _.unique(inputs);
40
41 // "Serialize" the data in the map
42111 tasks.push(function(callback) {
43111 callback(undefined, map.content());
44 });
45
46 // Minimize the content if flagged
47111 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
94111 var sourceMap = context.options.sourceMap;
95111 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
113111 tasks.push(function(data, callback) {
114111 fu.writeFile(output, data, callback);
115 });
116
117 // Excute everything and return to the caller
118111 async.waterfall(tasks, function(err) {
119111 if (err) {
1200 callback(new Error('Combined output "' + output + '" failed\n\t' + err));
1210 return;
122 }
123
124111 callback(undefined, {
125 fileName: output,
126 inputs: inputs,
127 warnings: warnings
128 });
129 });
130 }
131 }
132114 var completed = 0,
133 content = [];
134
135114 files.forEach(function(resource) {
136381 var fileInfo = context.loadResource(resource, function(err) {
137381 if (err && callback) {
1383 callback(err);
1393 callback = undefined;
1403 return;
141 }
142
143378 if (callback) {
144378 completed++;
145378 outputIfCompleted();
146 }
147 });
148381 content.push(fileInfo);
149 });
150};
151

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

93%
166
156
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) {
11148 this.options = options;
12148 this.mixins = [];
13148 this.configs = [];
14}
15
161Libraries.prototype.initialize = function(context, callback) {
17145 this.mixins = [];
18145 this.originalConfig = _.clone(context.config.attributes);
19
20145 function normalize(libraries) {
21290 if (_.isString(libraries)) {
222 return [libraries];
23 } else {
24288 return _.map(libraries, function (name) {
2582 if (_.isString(name)) {
2611 return path.normalize(name);
27 } else {
2871 return name;
29 }
30 });
31 }
32 }
33
34145 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
40145 delete context.config.attributes.mixins;
41
42145 async.forEachSeries(allLibraries, _.bind(this.load, this, context), callback);
43};
44
451Libraries.prototype.bowerLibraries = function(context) {
46146 try {
47146 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) {
65145 context.event.emit('debug', err);
66 }
67};
68
691Libraries.prototype.load = function(context, libraryConfig, callback) {
70 // Allow mixins to be passed directly
7187 var root = libraryConfig.root,
72 configPath,
73 self = this;
74
75 // Or as a file reference
7687 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
9787 if (root && !/\/$/.test(root)) {
9829 root = root + '/';
99 }
100
10187 if (!libraryConfig.name) {
1024 return callback(new Error('Mixin with root "' + root + '" is missing a name.'));
103 }
104
10583 var mixins = libraryConfig.mixins,
106 toRegister = {};
10783 delete libraryConfig.mixins;
108
10983 function mapMixin(mixin, name) {
110 // Only register once, giving priority to an explicitly defined mixin
11162 if (!toRegister[name]) {
11261 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
12583 _.each(mixins, mapMixin, this);
126
127 // Make mixin modules accessible as normal mixins as well
12883 _.each(libraryConfig.modules, mapMixin, this);
129
130 // After we've pulled everything in register
13183 _.each(toRegister, function(mixin, name) {
13261 this.mixins[name] = this.mixins[name] || [];
13361 var list = this.mixins[name];
13461 list.push(mixin);
135 }, this);
136
137 // Run all of the plugins that are concerned with this.
13883 libraryConfig.root = root;
13983 libraryConfig.path = configPath;
14083 context.loadedLibrary = libraryConfig;
14183 context.plugins.loadMixin(context, function(err) {
14283 delete libraryConfig.root;
143
144 // And then splat everything else into our config
14583 _.defaults(context.config.attributes, _.omit(context.loadedLibrary, 'name', 'path'));
146
14783 libraryConfig.serialize = function() {
1480 return { library: this.name };
149 };
150
15183 libraryConfig.root = root;
15283 self.configs.push(libraryConfig);
153
15483 callback(err);
155 });
156};
157
1581Libraries.prototype.findDecl = function(mixins, mixinName) {
15925 if (!mixinName.name) {
1604 mixinName = {name: mixinName};
161 }
162
16325 return _.find(mixins, function(mixinDecl) {
16429 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
171314 var mixins = _.clone(module.mixins || []),
172 processed = {};
173314 for (var i = 0; i < mixins.length; i++) {
174112 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
180110 if (mixinConfig) {
18140 mixinConfig = _.omit(mixinConfig, 'overrides', 'name', 'library');
182 }
183
184110 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
189110 var processedName = mixin.name + '_' + (mixin.parent && mixin.parent.name);
190110 if (!processed[processedName]) {
19192 processed[processedName] = true;
192
19392 _.each(mixin.attributes.mixins, function(mixinInclude) {
194 // Apply any attributes that were applied to the mixin config here
19518 if (mixinConfig) {
1964 mixinInclude = mixinInclude.name ? _.clone(mixinInclude) : {name: mixinInclude};
1974 _.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.
20218 mixinInclude.overrideLibrary = mixin.parent;
203
20418 if (!this.findDecl(mixins, mixinInclude)) {
20518 added.push(mixinInclude);
206 }
207 }, this);
208 }
209
210 // If we've found any new mixins insert them at the current spot and iterate
211 // over those items
212110 if (added.length > 2) {
21318 mixins.splice.apply(mixins, added);
21418 i--;
215 }
216 }
217
218 // Extend the module with each of the mixins content, giving priority to the module
219312 return _.map(mixins.reverse(), function(mixin) {
22092 var mixinConfig = mixin.name && mixin,
221 name = mixin;
22292 if (mixinConfig) {
22336 mixinConfig = _.clone(mixinConfig);
22436 delete mixinConfig.library;
22536 delete mixinConfig.container;
226 }
22792 mixin = _.extend(
228 {},
229 this.getMixin(name),
230 mixinConfig);
23192 if (!mixin.attributes) {
2320 throw new Error('Mixin "' + (name.name || name) + '" is not defined.');
233 }
234
235 // Save a distinct instance of the config for resource extension
23692 if (mixinConfig) {
23736 mixinConfig = _.clone(mixinConfig);
23836 delete mixinConfig.overrides;
23936 delete mixinConfig.name;
240 }
241
24292 return {
243 library: mixin,
244 mixinConfig: mixinConfig
245 };
246 }, this);
247};
248
2491Libraries.prototype.mapFiles = function(value, library, config) {
250181 var files = _.map(value, function(resource) {
251275 return this.mapFile(resource, library, config);
252 }, this);
253454 files = _.filter(files, function(file) { return file; });
254
255180 return files;
256};
2571Libraries.prototype.mapFile = function(resource, library, config) {
258 // If explicitly declared the resource library takes precedence
259326 if (_.isString(resource.library || resource.mixin)) {
2603 library = this.getConfig(resource.library || resource.mixin);
2613 if (!library) {
2621 throw new Error('Mixin "' + (resource.library || resource.mixin) + '" not found');
263 }
2642 delete resource.mixin;
265 }
266
267325 return resources.map(resource, library, config);
268};
269
2701Libraries.prototype.mapPathToLibrary = function(src, library) {
27139 return resources.pathToLibrary(src, library);
272};
273
2741Libraries.prototype.getMixin = function(name) {
275204 var mixins = (this.mixins && this.mixins[name.name || name]) || [],
276 library = name.library || name.container;
277204 if (mixins.length > 1 && !library) {
2781 throw new Error(
279 'Duplicate mixins found for "' + (name.name || name) + '"'
280 + _.map(mixins, function(mixin) {
2812 return ' parent: "' + mixin.parent.name + '"';
282 }).join(''));
283 }
284
285203 if (library) {
2869 if (name.name === undefined) {
2870 var found = _.find(this.configs, function(config) {
2880 return config.name === library;
289 });
2900 if (!found) {
2910 throw new Error('Unable to find library "' + library + '"');
292 }
2930 return found;
294 }
295
2969 var found = _.find(mixins, function(mixin) {
29717 return mixin.parent.name === library;
298 });
2999 if (found) {
3008 return found;
301 } else {
3021 throw new Error('Mixin named "' + name.name + '" not found in library "' + library + '"');
303 }
304194 } else if (mixins.length === 1) {
305194 return mixins[0];
306 }
307};
3081Libraries.prototype.getConfig = function(name) {
30913 return _.find(this.configs, function(config) { return config.name === name; });
310};
311
3121Libraries.prototype.mergeHash = function(hashName, input, mixin, output) {
31386 if (mixin[hashName]) {
314 // Close the value to make sure that we are not overriding anything
31511 if (!output[hashName] || output[hashName] === input[hashName]) {
3169 output[hashName] = _.clone(input[hashName] || {});
317 }
31811 _.each(mixin[hashName], function(value, key) {
31917 if (!input[hashName] || !(key in input[hashName])) {
32013 output[hashName][key] = value;
321 }
322 });
32311 return true;
324 }
325};
3261Libraries.prototype.mergeFiles = function(fieldName, input, mixinData, output, library) {
32740 if (mixinData[fieldName]) {
3289 mixinData = _.isArray(mixinData[fieldName]) ? mixinData[fieldName] : [mixinData[fieldName]];
329
3309 var configData = input[fieldName] || [];
3319 if (!output[fieldName] || configData === output[fieldName]) {
3327 output[fieldName] = _.clone(configData);
333 }
3349 if (!_.isArray(configData)) {
3352 configData = [configData];
336 }
3379 if (!_.isArray(output[fieldName])) {
3381 output[fieldName] = [output[fieldName]];
339 }
340
341 // Insert point is at the start of the upstream list, which we are
342 // assuming occurs at length postions from the end.
3439 _.each(mixinData, function(value) {
344 //Make the include relative to the mixin
34512 value = (library.root || '') + value;
346
34712 output[fieldName].splice(
348 output[fieldName].length - configData.length,
349 0,
350 {src: value, library: library});
351 });
352
3539 return true;
354 }
355};
356
3571module.exports = Libraries;
358

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

87%
64
56
8
LineHitsSource
11var _ = require('underscore'),
2 ChildPool = require('child-pool'),
3 Context = require('./context'),
4 EventEmitter = require('events').EventEmitter,
5 fs = require('fs'),
6 stateMachine = require('./state-machine'),
7 WatchManager = require('./watch-manager');
8
91exports.build = require('./build');
101exports.fileUtil = require('./fileUtil');
111exports.plugin = require('./plugin').plugin;
121exports.combine = require('./jsCombine').combine;
131exports.config = require('./config');
14
15/**
16 *
17 * @name init
18 * @function This function initializes a Lumbar instance
19 * @param {string} lumbarFile The lumbarFile is the main
20 * file. Its responsible to define all the platforms,
21 * packages, modules, and templates for Lumbar to use.
22 * @param {Object} options supports the following options:
23 * packageConfigFile (string): name of the package config file.
24 * outdir (string): path to directory of where to output the files.
25 * minimize (boolean): Should we minimize the files?
26 * @return {Object.<Function>}
27 */
281exports.init = function(lumbarFile, options) {
29 // Clone so we can mutate in the use API
3026 options = _.clone(options || {});
3126 options.plugins = _.clone(options.plugins || []);
32
3326 function logError(err) {
3459 if (err) {
353 event.emit('error', err);
36 }
37 }
38
3926 var event = new EventEmitter(),
40 watch,
41 watchContext;
42
4326 function watchOutputHandler(status) {
44104 if (!watch) {
45 // We've been cleaned up but residuals may still exist, do nothing on this exec
4615 return;
47 }
48
4989 if (status.fileConfig.isPrimary) {
5035 delete status.fileConfig;
5154 } else if (status.fileConfig.isPrimary === false) {
52 // This config is directly linked to another meaning we don't want to watch on it as
53 // it will be rebuilt.
5412 return;
55 }
56
5777 var originalContext = watchContext;
5877 watch.moduleOutput(status, function() {
5936 if (watchContext !== originalContext) {
60 // Ignore builds that may have occured at the same time as a config file change (i.e. a branch switch)
610 return;
62 }
63
6436 stateMachine.buildPlatform(watchContext.clone(status), logError);
65 });
66 }
67
6826 return _.extend(event, {
69 use: function(plugin) {
70 // Only has impact before exec
710 options.plugins.push(plugin);
72 },
73 /**
74 *
75 * @name build
76 * @function This function builds out the package(s).
77 * @param {string} packageName the name of the package listed under
78 * 'packages' from the lumbarFile passed in during the call to init().
79 * @param {Function} callback the node process Function
80 */
81 build: function(packageName, modules, callback) {
8211 stateMachine.loadConfig(lumbarFile, event, options, function(err, context) {
8311 if (err) {
840 if (!callback) {
850 throw err;
86 }
870 return callback(err);
88 }
89
9011 stateMachine.buildPackages(context, packageName, modules, callback);
91 });
92 },
93 watch: function(packageName, modules, callback) {
9419 if (!fs.watch) {
950 throw new Error('Watch requires fs.watch, introduced in Node v0.6.0');
96 }
97
9819 ChildPool.isBackground(true);
99
10019 watch = new WatchManager(event);
10119 watch.on('watch-change', function(info) {
10244 event.emit('watch-change', info);
103 });
104
10519 var self = this;
10619 stateMachine.loadConfig(lumbarFile, event, options, function(err, context) {
10719 if (err) {
1080 logError(err);
109 }
110
11119 if (!callback) {
11219 callback = modules;
11319 modules = undefined;
114 }
115
11619 watchContext = context;
117
118 // Watch for changes in the config file
11924 var mixinPaths = _.filter(_.pluck(context.libraries.configs, 'path'), function(path) { return path; });
12019 watch.configFile(lumbarFile, mixinPaths, function() {
1214 watchContext = undefined;
1224 self.watch(packageName, callback);
123 });
124
125 // If we have errored do not exec everything as it could be in an indeterminate state
12619 if (err) {
1270 return;
128 }
129
130 // Watch the individual components
13119 event.removeListener('output', watchOutputHandler);
13219 event.on('output', watchOutputHandler);
133
134 // Actual build everything
13519 var packages = packageName ? [packageName] : context.config.packageList();
13619 packages.forEach(function(name) {
13723 stateMachine.buildPackages(context, name, modules, logError);
138 });
139 });
140 },
141 unwatch: function() {
14215 event.removeListener('output', watchOutputHandler);
14315 if (watch) {
14415 watch.removeAllListeners();
14515 watch.reset();
14615 watch = undefined;
14715 watchContext = undefined;
148 }
149 }
150 });
151};
152

/Users/kpdecker/dev/walmart/lumbar/lib/path-relative.js

89%
29
26
3
LineHitsSource
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
221var path = require('path');
23
24// path.relative(from, to)
25// posix version
261exports.relative = function(from, to) {
2728 from = path.resolve(from).substr(1);
2828 to = path.resolve(to).substr(1);
29
3028 function trim(arr) {
3156 var start = 0;
3256 for (; start < arr.length; start++) {
3356 if (arr[start] !== '') {
3456 break;
35 }
36 }
37
3856 var end = arr.length - 1;
3956 for (; end >= 0; end--) {
4056 if (arr[end] !== '') {
4156 break;
42 }
43 }
44
4556 if (start > end) {
460 return [];
47 }
4856 return arr.slice(start, end - start + 1);
49 }
50
5128 var fromParts = trim(from.split('/'));
5228 var toParts = trim(to.split('/'));
53
5428 var length = Math.min(fromParts.length, toParts.length);
5528 var samePartsLength = length;
5628 for (var i = 0; i < length; i++) {
57156 if (fromParts[i] !== toParts[i]) {
580 samePartsLength = i;
590 break;
60 }
61 }
62
6328 var outputParts = [];
6428 for (var i = samePartsLength; i < fromParts.length; i++) {
652 outputParts.push('..');
66 }
67
6828 outputParts = outputParts.concat(toParts.slice(samePartsLength));
69
7028 return outputParts.join('/');
71};
72

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

99%
109
108
1
LineHitsSource
11var _ = require('underscore'),
2 path = require('path');
31const corePlugins = [
4 'mixin', 'amd',
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 'inline-styles-resources', 'styles', 'scripts', 'static'
11];
121var fileUtils = require("/Users/kpdecker/dev/walmart/lumbar/lib/./fileUtil");
13
141var globalPlugins = {};
15
161exports.plugin = function(name, plugin) {
1721 globalPlugins[name] = plugin;
1821 plugin.id = name;
19};
20
211exports.plugin('amd', require('./plugins/amd'));
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) {
44138 var plugins;
45138 var modes; // all registered modes
46138 var pluginModes; // map of modes and plugins scoped to the mode
47138 var modeAll; // plugins that are scoped to all modes
48
49138 function runPlugins(context, methodName, complete, failOver, noMode) {
503076 var len = 0,
51 pluginMode = pluginModes[context.mode] || [];
52
533076 return (function next(complete) {
54 /*jshint boss:true */
559143 var plugin;
569143 while (plugin = plugins[len++]) {
57 // if plugin shouldn't work with current mode, go to next
5859937 if (!noMode
59 && (!context.mode || pluginMode.indexOf(plugin) < 0)
60 && modeAll.indexOf(plugin) < 0) {
6124977 continue;
62 }
63
6434960 var method = plugin[methodName];
6534960 if (method) {
666769 if (complete) {
676627 process.nextTick(function() {
686627 method.call(plugin, context, next, complete);
69 });
706627 return;
71 } else {
72142 return method.call(plugin, context, next, complete);
73 }
74 }
75 }
76
77 // We're done, send data back
782374 if (complete) {
79 // async
80 // Clear out our stack under async mode to try to keep the stack somewhat sane.
812184 process.nextTick(function() {
822184 complete(undefined, failOver && failOver());
83 });
84 } else {
85 // sync
86190 return failOver && failOver();
87 }
88 })(complete);
89 }
90
91138 function registerPlugin(plugin) {
922713 var _plugin = globalPlugins[plugin] || plugin;
93
942713 var mode = _plugin.mode;
952713 if (mode) {
962443 if (_.isString(mode)) {
971896 mode = [mode];
98 }
992443 _.each(mode, function(_mode) {
1002990 if (mode === 'all') {
101 // allow plugins to contribute new modes and participate in all modes
1020 modeAll.push(_plugin);
103 } else {
1042990 if (modes.indexOf(_mode) < 0) {
105412 modes.push(_mode);
106412 pluginModes[_mode] = [];
107 }
1082990 pluginModes[_mode].push(_plugin);
109 }
110 });
111 } else {
112270 modeAll.push(_plugin);
113 }
1142713 plugins.push(_plugin);
1152713 plugins.sort(function(a, b) {
11640953 return (a.priority || 50) - (b.priority || 50);
117 });
118 }
119
120138 return {
121 get: function(name) {
122 // Find the plugin with this id, if one exists
12372 var plugin = plugins.reduce(function(plugin, left) {
1241368 return plugin.id === name ? plugin : left;
125 });
126
127 // If the plugin was not found do not return the last item in the reduce
12872 if (plugin.id === name) {
12970 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
150138 plugins = [];
151138 modes = []; // all registered modes
152138 pluginModes = {}; // map of modes and plugins scoped to the mode
153138 modeAll = []; // plugins that are scoped to all modes
154
155 // load the core plugins
156138 if (!options.ignoreCorePlugins) {
157135 corePlugins.forEach(registerPlugin);
158 }
159
160138 var self = this;
161138 function plugin(plugins) {
162276 if (plugins) {
16335 plugins.forEach(self.use, self);
164 }
165 }
166
167 // load command line plugins
168138 plugin(options.plugins);
169
170 // load lumbar.json plugins
171138 plugin(config.attributes.plugins);
172 },
173
174 loadMixin: function(context, complete) {
17583 runPlugins(context, 'loadMixin', complete, undefined, true);
176 },
177 loadConfig: function(context, complete) {
178137 runPlugins(context, 'loadConfig', complete, undefined, true);
179 },
180 outputConfigs: function(context, complete) {
181243 runPlugins(context, 'outputConfigs', complete, function() {
182 // Default to a one to one mapping for a given {platform, package, module, mode} combo
183241 return [ {} ];
184 });
185 },
186 modeComplete: function(context, complete) {
187188 runPlugins(context, 'modeComplete', complete);
188 },
189 fileName: function(context, complete) {
190255 runPlugins(context, 'fileName', complete);
191 },
192
193 fileFilter: function(context) {
194332 return runPlugins(context, 'fileFilter');
195 },
196 moduleResources: function(context, complete) {
197448 runPlugins(context, 'moduleResources', complete, function() {
198245 var module = context.module;
199245 return (module[context.mode] || []).slice();
200 });
201 },
202 resourceList: function(context, complete) {
2031020 runPlugins(context, 'resourceList', complete, function() { return [context.resource]; });
204 },
205
206 file: function(context, complete) {
207146 runPlugins(context, 'file', complete);
208 },
209 module: function(context, complete) {
210278 runPlugins(context, 'module', complete);
211 },
212 resource: function(context, complete) {
213815 runPlugins(context, 'resource', complete, function() { return context.resource; });
214 },
215 modes: function() {
21648 return modes;
217 }
218 };
219};
220

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

96%
161
156
5
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 build = require('../build'),
4 defineParser = require('../util/define-parser'),
5 fu = require('../fileUtil'),
6 path = require('path'),
7 join = path.join,
8 resources = require('../util/resources'),
9 templateUtil = require('../templateUtil');
10
111module.exports = {
12 loaders: {},
13 generator: generator,
14 registerOutput: function(path, context) {
1520 var appId = context.platformCache.amdAppModules[path],
16 fileId = context.fileCache.amdFileModules[path],
17 ret = 'lwmd[' + fileId + '] = ';
18
1920 if (!_.isNumber(fileId)) {
200 throw new Error('Missing module definition for ' + path);
21 }
22
2320 if (appId) {
242 ret = 'wmd[' + appId + '] = ' + ret;
25 }
26
2720 return generator(ret);
28 },
29
30 priority: 2, // After mixins
31
32 resourceList: function(context, next, complete) {
33555 if (context.config.attributes.amd
34 && build.filterResource(context.resource, context)) {
3546 next(function(err, resources) {
3646 if (err) {
371 return complete(err);
38 }
39
4045 if (context.config.isAppModule(context.module) && !context.platformCache.amdAppModules) {
411 context.platformCache.amdAppModules = {};
42 }
4345 if (!context.platformCache.amdModuleCache) {
4440 context.platformCache.amdModuleCache = {};
45 }
4645 if (!context.fileCache.amdFileModules) {
471 context.fileCache.amdFileModules = {};
48 }
49
5045 async.map(
51 resources,
52 function(resource, callback) {
5345 parseFile(resource, undefined, context, true, callback);
54 },
55 function(err, data) {
5645 if (data) {
5745 data = _.compact(_.flatten(data));
58 }
5945 complete(err, data);
60 });
61 });
62 } else {
63509 next(complete);
64 }
65 }
66};
67
681module.exports.loaders.view = {
69 resource: function(name) {
7054 return 'js/views/' + name + '.js';
71 },
72 amdName: function(path) {
7388 if (/js\/views\/(.*)\.js$/.exec(path)) {
741 return 'view!' + RegExp.$1;
75 }
76 },
77
78 canOutput: function(data, resource, context) {
7965 return data.view;
80 },
81 output: function(defined, isAppModule, dependencies, context) {
821 var viewName = templateUtil.escapeJsString(defined.name.replace(/^.*!/, '')),
83 fileId = context.fileCache.amdFileModules[defined.resourceName];
84
851 return [
86 module.exports.registerOutput(defined.resourceName, context),
87 generator('Thorax.Views["' + viewName + '"] = ('),
88 generator(defined.source),
89 generator(')(' + dependencies + ');\n'),
90
91 generator('lwmd[' + fileId + '].prototype.name = "' + viewName + '";\n')
92 ];
93 }
94};
95
961module.exports.loaders.router = {
97 resource: function(name) {
980 return 'js/routers/' + name + '.js';
99 },
100 amdName: function(path) {
10187 if (/js\/routers\/(.*)\.js$/.exec(path)) {
1020 return 'router!' + RegExp.$1;
103 }
104 },
105
106 diffDefine: function(oldConfig, newConfig, context) {
107 // Look only at the routes
1086 oldConfig = _.keys((oldConfig && oldConfig.routes) || {});
1096 newConfig = _.keys((newConfig && newConfig.routes) || {});
110
1116 if (!_.isEqual(oldConfig, newConfig)) {
1125 context.event.emit('module-map');
113 }
114 },
115
116 canOutput: function(data, resource, context) {
11726 return data.router;
118 },
119 output: function(defined, isAppModule, dependencies, context) {
1205 var viewName = templateUtil.escapeJsString(defined.name.replace(/^.*!/, '')),
121 fileId = context.fileCache.amdFileModules[defined.resourceName];
122
1235 return [
124 generator('module.routes = '),
125 generator(JSON.stringify(defined.routes)),
126 generator(';\n'),
127 module.exports.registerOutput(defined.resourceName, context),
128 generator('Phoenix.Router.create(module, ('),
129 generator(defined.source),
130 generator(')(' + dependencies + '));\n')
131 ];
132 }
133};
134
135// Left out of the loaders object so we do not have to worry about enumaration order
1361module.exports.defaultLoader = {
137 resource: function(name, resource, context) {
13844 if (name.indexOf('.') === -1) {
13944 return 'js/' + name + '.js';
140 }
1410 return resource;
142 },
143 amdName: function(path) {
14485 if (/js\/(.*)\.js$/.exec(path)) {
14540 return RegExp.$1;
146 } else {
14745 return path;
148 }
149 },
150
151 output: function(defined, isAppModule, dependencies, context) {
15213 if (!defined.amd) {
1531 return generator(defined.source);
154 } else {
15512 return [
156 module.exports.registerOutput(defined.resourceName, context),
157 generator('(' + defined.source),
158 generator(')(' + dependencies + ');\n')
159 ];
160 }
161 },
162 lookup: function(name, path, isAppModule, context) {
163 // WARN : We are assuming that the dependency has been looked up at some point
16450 var appId = context.platformCache.amdAppModules[path],
165 fileId = context.fileCache.amdFileModules[path],
166 lookup = 'lwmd[',
167 id = fileId;
168
16950 if (appId) {
1706 lookup = 'wmd[';
1716 id = appId;
172 }
17350 if (id) {
17442 return lookup + id + ']';
175 }
176 }
177};
178
1791function parseFile(resource, library, context, isRoot, complete) {
180 /*jshint eqnull: true */
181101 var cache;
182
183101 var resourceName = resources.source(resource);
184101 if (!resourceName || !_.isString(resourceName) || resource.amd === false) {
1851 return complete(undefined, resource);
186 }
187
188100 var loaderInfo = loaderFromName(resourceName, context),
189 loader = loaderInfo.loader,
190 amdName = loaderInfo.amdName;
191
192100 library = loaderInfo.library || library || resource.library;
193
194100 if (!isRoot && loader && loader.resource) {
19556 resource = loader.resource(loaderInfo.name, resource, context);
196
197 // Map the returned resource name to the library
19856 resourceName = join((library && library.root) || '', resources.source(resource));
199 }
200
201 // Remap the amdName for the current library
202100 if (library && amdName.indexOf(':') < 0) {
20316 amdName = amdName.replace(/^(.*!)?(.*)$/, function(match, loader, name) { return (loader || '') + library.name + ':' + name; });
204 }
205
206100 var isAppModule = context.config.isAppModule(context.module),
207 isTopModule = !isAppModule && context.config.isTopLevel(context.module),
208 inApp = context.platformCache.amdAppModules && (context.platformCache.amdAppModules[resourceName] != null),
209 inFile = context.fileCache.amdFileModules[resourceName] != null,
210 appId = isAppModule ? _.keys(context.platformCache.amdAppModules).length + 1 : 0,
211 fileId = _.keys(context.fileCache.amdFileModules).length + 1;
212
213100 if (!isRoot && ((!isTopModule && inApp) || inFile)) {
21430 return complete();
215 }
216
21770 if (!resource.global && /\bjs\/.*\.js$/.test(resourceName)) {
218 // Handle any overrides as necessary
21966 resource = resources.map(resource, library);
22066 var fileName = resources.source(resource);
221
222 // Flag state now so we don't read multiple times due to IO wait in the readFile call
22366 if (isAppModule) {
2245 context.platformCache.amdAppModules[resourceName] = true;
225 }
22666 context.fileCache.amdFileModules[resourceName] = true;
227
22866 cache = fu.readFileArtifact(fileName, 'amd', function(err, data) {
22966 if (err) {
2301 return complete(err);
231 }
23265 var cache = data.artifact;
233
234 // If we have something in the cache, shortcircuit the parsing
23565 if (cache === undefined) {
23665 try {
23765 data = defineParser(data.data.toString(), {file: fileName});
238 } catch (err) {
2392 context.event.emit('log', 'Failed to parse AMD file: ' + fileName + ' ' + err);
2402 data = false;
241 }
242
24365 if (data) {
24463 cache = {
245 defined: _.map(data, function(data) {
24665 var loader = _.find(module.exports.loaders, function(loader) {
247137 return loader.canOutput && loader.canOutput(data, resource, context);
248 }) || module.exports.defaultLoader;
249
25065 var ret = _.omit(data, 'define');
25165 if (data.define) {
25261 ret.amd = data.define;
25361 ret.name = ret.name || amdName;
25461 ret.resourceName = resourceName;
255 }
25665 ret.loader = loader;
25765 return ret;
258 }),
259 dependencies: _.compact(_.flatten(_.pluck(data, 'deps')))
260 };
261
262 // If we have no AMD content then leave this as is
263128 var defines = _.filter(cache.defined, function(define) { return define.amd; });
26463 if (!defines.length) {
2653 cache = false;
26660 } else if (defines.length > 1) {
2671 return complete(new Error('Multiple modules defined in "' + fileName + '"'));
268 } else {
26959 context.event.emit('debug', 'Dependencies ' + cache.dependencies + ' found for ' + amdName);
270 }
271
27262 defines = defines[0];
273
27462 var oldConfig = context.platformCache.amdModuleCache[fileName];
27562 var loader = defines && defines.loader;
27662 if (loader && loader.diffDefine) {
2775 loader.diffDefine(oldConfig, defines, context);
278 }
27962 if (oldConfig && oldConfig.loader && oldConfig.loader.diffDefine && oldConfig.loader !== loader) {
2801 oldConfig.loader.diffDefine(oldConfig, defines, context);
281 }
28262 context.platformCache.amdModuleCache[fileName] = defines;
283 } else {
2842 cache = false;
2852 context.platformCache.amdModuleCache[fileName] = undefined;
286 }
287
28864 fu.setFileArtifact(fileName, 'amd', cache);
289 }
29064 if (isAppModule) {
2915 context.platformCache.amdAppModules[resourceName] = cache && appId;
292 }
29364 context.fileCache.amdFileModules[resourceName] = cache && fileId;
294
29564 async.map(
296 cache.dependencies || [],
297 function(resource, callback) {
29856 parseFile(resource, library, context, false, callback);
299 },
300 function(err, data) {
30164 if (data) {
30264 if (cache) {
303 // If we have AMD content then handle it
30459 _.each(cache.defined, function(defined) {
30560 var dependencies = lookupDepenencies(defined.deps, isAppModule, context),
306 output = defined.loader.output || module.exports.defaultLoader.output;
30760 data.push(output(defined, isAppModule, dependencies, context));
308 });
309 } else {
310 // Otherwise pass through
3115 data.push(resource);
312 }
313
314 // Reduce everything to a tidy list
31564 data = _.compact(_.flatten(data));
316 }
317
31864 complete(err, data);
319 });
320 });
321 } else {
3224 if (library) {
3231 resource = resources.map(resource, library);
324 }
325
3264 complete(undefined, resource);
327 }
328}
329
3301function loaderFromName(name, context) {
331156 var amdName = name,
332 loader = module.exports.defaultLoader,
333 library;
334156 if (/^(.*)!(.*)$/.test(name)) {
33568 amdName = name;
336
33768 var loaderName = RegExp.$1;
33868 loader = module.exports.loaders[loaderName] || loader,
339 name = RegExp.$2;
340 } else {
34188 var remapedName = amdName;
34288 _.find(module.exports.loaders, function(loader) {
343353 return remapedName = (loader.amdName && loader.amdName(name));
344 });
34588 amdName = remapedName || module.exports.defaultLoader.amdName(amdName);
346 }
347
348156 if (/^(.*):(.*)$/.test(name)) {
34910 library = context.libraries.getConfig(RegExp.$1);
35010 name = RegExp.$2;
351 }
352
353156 return {
354 loader: loader,
355 amdName: amdName,
356 name: name,
357 library: library
358 };
359}
360
3611function lookupDepenencies(dependencies, isAppModule, context) {
36260 return _.map(dependencies, function(dep) {
36356 var loaderInfo = loaderFromName(dep, context);
364
36556 dep = resources.source(loaderInfo.loader.resource(loaderInfo.name, undefined, context));
36656 if (loaderInfo.loader.lookup) {
36728 return loaderInfo.loader.lookup(loaderInfo.name, dep, isAppModule, context) || 'undefined';
368 }
369
370 // Lookup javascript resources using normal means if the loader does not define any helpers
37128 if (/\.js$/.test(dep)) {
37228 return module.exports.defaultLoader.lookup(loaderInfo.name, dep, isAppModule, context) || 'undefined';
373 }
374
3750 return 'undefined';
376 }).join(', ');
377}
378
3791function generator(string) {
380 // TODO : Handle proper source mapping here
38187 var ret = function(context, callback) { callback(undefined, {data: string, generated: true, noSeparator: true}); };
38287 ret.stringValue = string;
38387 ret.sourceFile = undefined;
38487 ret.ignoreWarnings = true;
38587 return ret;
386}
387

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

100%
14
14
0
LineHitsSource
11var CoffeeScript = require('coffee-script'),
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) {
10269 var resource = context.resource;
11269 if (/\.coffee$/.test(resource.src)) {
12
132 next(function(err, resource) {
142 function generator(context, callback) {
15 // Load the source data
162 context.loadResource(resource, function(err, file) {
172 if (err) {
181 return callback(err);
19 }
20
21 // Update the content
221 callback(err, {
23 data: CoffeeScript.compile(file.content.toString()),
24 inputs: file.inputs
25 });
26 });
27 }
28
29 // Include any attributes that may have been defined on the base entry
302 if (!_.isString(resource)) {
312 _.extend(generator, resource);
32 }
332 complete(undefined, generator);
34 });
35 } else {
36267 next(complete);
37 }
38 }
39};
40

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

91%
109
100
9
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 amd = require('./amd'),
21 handlebars = require('handlebars'),
22 path = require('path'),
23 resources = require('../util/resources'),
24 templateUtil = require('../templateUtil');
25
261handlebars.registerHelper('without-extension', function(str) {
271 return str.replace(/\.[a-zA-Z0-9]+$/, '');
28});
29
30
311const DEFAULT_TEMPLATE_TEMPLATE = "/* handsfree : {{{name}}}*/\n{{{templateCache}}}['{{{name}}}'] = {{handlebarsCall}}({{{data}}});\n";
32
331function ensureTemplateTemplates(context, complete) {
3440 if (!context.configCache.templateTemplate) {
3514 var templateTemplate = (context.config.attributes.templates && context.config.attributes.templates.template) || DEFAULT_TEMPLATE_TEMPLATE;
3614 context.fileUtil.loadTemplate(templateTemplate, false, function(err, compiled) {
3714 if (err) {
381 complete(err);
39 } else {
4013 context.configCache.templateTemplate = compiled;
4113 complete();
42 }
43 });
44 } else {
4526 complete();
46 }
47}
48
491function loadTemplate(src, resource, context, callback) {
5040 ensureTemplateTemplates(context, function(err) {
5140 if (err) {
521 return callback(err);
53 }
5439 var artifactType = 'template' + context.fileConfig.server;
5539 context.fileUtil.readFileArtifact(src, artifactType, function(err, cache) {
5639 if (err) {
570 callback(new Error('Failed to load template "' + src + '"\n\t' + err));
580 return;
59 }
60
6139 var artifact = cache.artifact || {},
62 data = artifact.data || cache.data.toString(),
63 attr = context.config.attributes,
64 templates = attr.templates || {},
65 appModule = context.config.scopedAppModuleName(context.module),
66 templateCache = (attr.templates && attr.templates.cache)
67 || attr.templateCache
68 || ((appModule ? appModule + '.' : '') + 'templates'),
69 template = context.configCache.templateTemplate;
70
71 // Figure out what this file is called. This could vary due to prefixing and overriding
7239 var name = context.libraries.mapPathToLibrary(src, resource.library);
7339 if (templates.root && name.indexOf(templates.root) === 0) {
744 name = name.substring(templates.root.length);
75 }
7639 name = templateUtil.escapeJsString(name);
77
78 // We have the template data, now convert it into the proper format
7939 if (!cache.artifact) {
8018 if (templates.precompile) {
812 var options = context.fileCache.precompileTemplates;
822 if (!options) {
832 context.fileCache.precompileTemplates = options = _.clone(templates.precompile);
842 if (templates.knownHelpers || options.knownHelpers) {
850 var helpersList = _.union(
86 options.knownHelpers || templates.knownHelpers,
87 context.fileCache.knownHelpers);
88
890 options.knownHelpers = helpersList.reduce(
90 function(value, helper) {
910 value[helper] = true;
920 return value;
93 }, {});
94 }
952 if (context.fileConfig.server && templates.server) {
961 _.extend(options, templates.server);
97 }
98 }
992 try {
1002 data = handlebars.precompile(data, options);
101 } catch (err) {
1020 return callback(new Error('Template "' + name + '": ' + err.message));
103 }
104 } else {
10516 data = "'" + templateUtil.escapeJsString(data) + "'";
106 }
10718 context.fileUtil.setFileArtifact(src, artifactType, {data: data, template: template});
108 }
109
11039 callback(
111 undefined,
112 template({
113 name: name,
114 handlebarsCall: templates.precompile ? 'Handlebars.template' : 'Handlebars.compile',
115 templateCache: templateCache,
116 data: data
117 })
118 );
119 });
120 });
121}
122
1231module.exports = {
124 mode: 'scripts',
125 priority: 50,
126
127 loadMixin: function(context, next, complete) {
12883 var mixinTemplates = context.loadedLibrary.templates;
12983 if (mixinTemplates) {
13014 var templates = context.libraries.originalConfig.templates || {},
131 configTemplates = _.clone(context.config.attributes.templates || templates),
132 assigned = false;
133
13414 ['template', 'precompile', 'cache', 'root'].forEach(function(key) {
13556 if (_.has(mixinTemplates, key) && !_.has(templates, key)) {
13610 configTemplates[key] = mixinTemplates[key];
13710 assigned = true;
138 }
139 });
140
14114 if (_.has(mixinTemplates, 'knownHelpers')) {
1421 configTemplates.knownHelpers = (configTemplates.knownHelpers || []).concat(mixinTemplates.knownHelpers);
1431 assigned = true;
144 }
145
14614 if (assigned) {
1477 context.config.attributes.templates = configTemplates;
148 }
149 }
15083 next(complete);
151 },
152
153 resource: function(context, next, complete) {
154269 var resource = context.resource;
155
156269 if (/\.handlebars$/.test(resource.src) || resource.template) {
15731 var loadedTemplates = context.fileCache.loadedTemplates;
15831 if (!loadedTemplates) {
15927 loadedTemplates = context.fileCache.loadedTemplates = {};
160 }
161
16231 var generator = function(buildContext, callback) {
16328 var output = [],
164 inputs = [];
16528 context.fileUtil.fileList(resource.src, /\.handlebars$/, function(err, files) {
16628 if (err) {
1670 callback(err);
1680 return;
169 }
170
17128 function ignore(file) {
172121 return file.dir || loadedTemplates[resources.source(file)];
173 }
17428 function checkComplete() {
17567 if (inputs.length === files.length) {
176 // Sorting is effectively sorting on the file name due to the name comment in the template
17727 callback(undefined, {
178 inputs: inputs,
179 data: output.sort().join(''),
180 name: resource.src,
181 generated: true,
182 noSeparator: true,
183 ignoreWarnings: true
184 });
18527 return true;
186 }
187 }
188
18949 inputs = _.map(files.filter(ignore), function(input) { return input.src || input; });
19028 if (checkComplete()) {
1911 return;
192 }
193
19427 files.forEach(function(file) {
19560 if (ignore(file)) {
19620 return;
197 }
198
19940 var src = file.src || file;
20040 loadedTemplates[src] = true;
20140 loadTemplate(src, resource, context, function(err, data) {
20240 if (err) {
2031 return callback(err);
204 }
205
20639 output.push(data.data || data);
20739 inputs.push(src);
20839 checkComplete();
209 });
210 });
211 });
212 };
21331 generator.sourceFile = resource.src;
21431 complete(undefined, generator);
215 } else {
216238 next(complete);
217 }
218 }
219};
220
2211amd.loaders.hbs = {
222 resource: function(name, resource, context) {
2234 var attr = context.config.attributes,
224 templates = attr.templates || {};
225
2264 return {src: path.join(templates.root || '', name + '.handlebars')};
227 },
228 lookup: function(name) {
2292 return 'Handlebars.templates["' + templateUtil.escapeJsString(name) + '"]';
230 }
231};
232
2331amd.loaders.helper = {
234 resource: function(name, resource, context) {
2352 return 'js/helpers/' + name + '.js';
236 },
237 amdName: function(name) {
23887 if (/js\/helpers\/(.*)\.js$/.exec(name)) {
2392 return 'helpers!' + RegExp.$1;
240 }
241 },
242
243 canOutput: function(data, resource, context) {
24421 return data.helper;
245 },
246 output: function(defined, isAppModule, dependencies, context) {
2472 var helperName = templateUtil.escapeJsString(defined.name.replace(/^.*!/, '')),
248 fileId = context.fileCache.amdFileModules[defined.resourceName],
249 knownHelpers;
250
251 // Update known helpers list
2522 if (isAppModule) {
253 // Silently dropping on the floor if the knownHelpers feature is not enabled
2541 var attr = context.config.attributes,
255 templates = attr.templates || {};
2561 knownHelpers = templates.knownHelpers || [];
257 } else {
2581 knownHelpers = context.fileCache.knownHelpers = context.fileCache.knownHelpers || [];
259 }
2602 knownHelpers.push(helperName);
261
2622 return [
263 amd.registerOutput(defined.resourceName, context),
264 amd.generator('('),
265 amd.generator(defined.source),
266 amd.generator(')(' + dependencies + '));\n'),
267 amd.generator('Handlebars.registerHelper("' + helperName + '", lwmd[' + fileId + ']);\n')
268 ];
269 }
270};
271

/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) {
8363 if (inlineStyles.isInline(context) && context.mode === 'styles') {
9 // Prevent stylesheet output if in inline mode
103 complete(undefined, []);
11360 } else if (inlineStyles.isInline(context)) {
126 next(function(err, scripts) {
136 complete(undefined, scripts.concat(context.module.styles || []));
14 });
15 } else {
16354 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) {
161541 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) {
2583 var mixinStyles = context.loadedLibrary.styles;
2683 if (mixinStyles) {
2720 var styles = context.libraries.originalConfig.styles || {},
28 configStyles = _.clone(context.config.attributes.styles || styles),
29 assigned = false;
30
3120 ['inline', 'inlineLoader'].forEach(function(key) {
3240 if ((key in mixinStyles) && !(key in styles)) {
336 configStyles[key] = mixinStyles[key];
34
356 assigned = true;
36 }
37 });
38
3920 if (assigned) {
405 context.config.attributes.styles = configStyles;
41 }
42 }
4383 next(complete);
44 },
45
46 outputConfigs: function(context, next, complete) {
47194 if (isInline(context) && context.mode === 'styles') {
48 // Prevent stylesheet output if in inline mode
492 complete(undefined, []);
50 } else {
51192 next(complete);
52 }
53 },
54
55 module: function(context, next, complete) {
56196 next(function(err) {
57196 if (err) {
580 return complete(err);
59 }
60
61196 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
94196 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) {
5202 if (context.config.attributes.filterDuplicates === false) {
62 return context.moduleResources;
7 }
8
9200 var paths = {};
10200 return _.filter(context.moduleResources, function(resource) {
11388 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 }
18386 return true;
19 });
20}
21
221function combineResources(context, outputData, callback) {
23172 var resources = context.resources || [];
24172 if (!resources.length) {
2552 return callback();
26 }
27
28120 context.outputFile(function(callback) {
29120 lumbar.combine(
30 context,
31 resources,
32 context.fileName,
33 context.options.minimize && context.mode === 'scripts',
34 context.mode === 'styles',
35 function(err, data) {
36120 data = data || {};
37120 _.extend(data, outputData);
38
39120 if (!data.fileName) {
409 data.fileName = context.fileName;
41 }
42120 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
52120 callback(err, data);
53 });
54 },
55 callback);
56}
57
581module.exports = {
59 priority: 1,
60
61 modeComplete: function(context, next, complete) {
62133 next(function(err) {
63133 if (err) {
640 return complete(err);
65 }
66
67133 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 {
76103 complete();
77 }
78 });
79 },
80 module: function(context, next, complete) {
81202 next(function(err) {
82202 if (err) {
830 return complete(err);
84 }
85
86202 if (!context.combined) {
87142 context.resources = filterDuplicates(context);
88142 context.moduleResources = undefined;
89142 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) {
7134 var modules = context.config.attributes.modules,
8 errored;
9134 _.each(context.libraries.configs, function(library) {
10 // Import any modules that are not overriden in the core file
1178 _.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
29134 _.each(modules, function(module, name) {
30160 module.name = module.name || name;
31160 var mixins;
32160 try {
33160 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
40158 try {
41158 ['scripts', 'styles', 'static'].forEach(function(field) {
42472 var list = module[field];
43
44472 if (list) {
45126 module[field] = context.libraries.mapFiles(list);
46 }
47 });
48
49157 _.each(mixins, function(mixin) {
5046 var mixinConfig = mixin.mixinConfig,
51 library = mixin.library;
52
53 // Direct copy for any fields that are not already defined on the object.
5446 _.defaults(module, library.attributes);
55
56 // Merge known array/object types
5746 ['scripts', 'styles', 'static', 'routes'].forEach(function(field) {
58184 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
68134 _.each(_.keys(modules), function(name) {
69160 if (!modules[name]) {
701 delete modules[name];
71 }
72 });
73
74134 if (!errored) {
75131 next(complete);
76 }
77 }
78};
79
801function firstLocal(collection) {
8156 for (var i = 0, len = collection.length; i < len; i++) {
8271 if (!collection[i].global) {
8356 return i;
84 }
85 }
860 return i;
87}
88
891function mergeValues(module, field, library, mixinConfig, context) {
90184 var value = module[field],
91 mixinValue = library.attributes[field];
92
93184 if (!value) {
94131 return;
95 }
96
9753 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 }
10437 } else if (!_.isArray(value)) {
1055 _.defaults(value, mixinValue);
10632 } else if (mixinValue) {
10728 mixinValue = context.libraries.mapFiles(mixinValue, library, mixinConfig);
108
10928 var mixinFirstLocal = firstLocal(mixinValue),
110 moduleFirstLocal = firstLocal(value);
111
11228 if (mixinFirstLocal) {
1134 value.unshift.apply(value, mixinValue.slice(0, mixinFirstLocal));
114 }
11528 if (mixinFirstLocal < mixinValue.length) {
11628 var locals = mixinValue.slice(mixinFirstLocal);
11728 locals.unshift(mixinFirstLocal + moduleFirstLocal, 0);
11828 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() {
1119 if (!moduleMapTemplate) {
121 moduleMapTemplate = handlebars.compile(fs.readFileSync(__dirname + '/module-map.handlebars').toString());
13 }
1419 return moduleMapTemplate;
15}
16
17// Force template load before EMFILE may be an issue
181getModuleMapTemplate();
19
201function loadModuleMap(map, mapper, callback) {
2118 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.
2518 map = (function orderObject(map) {
26102 var ret = _.isArray(map) ? [] : {};
27102 _.keys(map).sort().forEach(function(key) {
28177 var value = map[key];
29177 ret[key] = _.isObject(value) ? orderObject(value) : value;
30 });
31102 return ret;
32 })(map);
33
3418 callback(
35 undefined,
36 moduleMapTemplate({
37 moduleMapper: mapper,
38 map: JSON.stringify(map)
39 })
40 );
41}
42
431function buildMap(context, callback) {
4436 if (context.combined) {
4515 moduleConfig(context, undefined, function(err, config, prefix) {
4615 callback(err, { base: config }, prefix);
47 });
48 } else {
4921 var attr = context.config.attributes || {},
50 app = attr.application || {},
51 modules = context.config.moduleList(context.package);
52
5321 var map = {modules: {}, routes: {}},
54 commonPrefix;
55
5621 async.forEach(modules, function(module, callback) {
5728 moduleConfig(context, module, function(err, config, prefix) {
5828 if (err) {
590 return callback(err);
60 }
61
6228 if (app.module === module) {
634 map.base = config;
64 } else {
6524 map.modules[module] = config;
66
6724 var routes = context.config.routeList(module);
6824 _.each(routes, function(value, route) {
6917 map.routes[route] = module;
70 });
71 }
7228 commonPrefix = findPrefix(prefix, commonPrefix);
73
7428 callback();
75 });
76 },
77 function(err) {
7821 callback(err, map, commonPrefix);
79 });
80 }
81}
82
831function stripPrefix(map, prefix) {
8418 if (!prefix) {
8517 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) {
11943 var ret = {},
120 commonPrefix,
121 preload = module && context.config.module(module).preload,
122 depends = module && context.config.module(module).depends;
12343 if (preload) {
1241 ret.preload = preload;
125 }
12643 if (depends) {
1274 ret.depends = depends;
128 }
12943 async.forEach([{key: 'js', mode: 'scripts'}, {key: 'css', mode: 'styles'}], function(obj, callback) {
13086 fileList(context, obj.mode, module, function(err, list, prefix) {
13186 ret[obj.key] = list;
13286 commonPrefix = findPrefix(prefix, commonPrefix);
13386 callback(err);
134 });
135 },
136 function(err) {
13743 callback(err, ret, commonPrefix);
138 });
139}
1401function fileList(context, mode, module, callback) {
141 // Check to see if we even have this type of resource
14286 var modules = !context.combined ? [ module ] : context.config.moduleList(context.package);
14386 async.some(modules, function(module, callback) {
144116 var resourceContext = context.clone();
145116 resourceContext.mode = mode;
146116 resourceContext.module = context.config.module(module);
147116 resourceContext.isModuleMap = true;
148
149116 resourceContext.plugins.moduleResources(resourceContext, function(err, resources) {
150116 callback((resources || []).length);
151 });
152 },
153 function(hasResource) {
15486 if (!hasResource) {
15511 return callback();
156 }
157
158 // Output the config
15975 context.fileNamesForModule(mode, module, function(err, configs) {
16075 if (err) {
1610 return callback(err);
162 }
163
16475 var prefix;
165176 configs = configs.filter(function(config) { return !config.server; });
166101 configs = configs.sort(function(a, b) { return a.pixelDensity - b.pixelDensity; });
16775 configs = configs.map(function(config, i) {
168101 var path = config.fileName.path,
169 ret = path + '.' + config.fileName.extension;
170
171101 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
182101 prefix = findPrefix(path, prefix);
183
184101 return ret;
185 });
186
18775 var ret;
18875 if (configs.length === 1) {
18950 ret = configs[0];
19025 } else if (configs.length) {
19125 ret = configs;
192 }
19375 callback(undefined, ret, prefix);
194 });
195 });
196}
197
1981function findPrefix(path, prefix) {
199 /*jshint eqnull:true*/
200215 if (path == null) {
20111 return prefix;
202 }
203204 if (prefix == null) {
204 // Ensure that we get 'x' for strings of type 'x/'
205139 prefix = dirname(path + 'a') + '/';
206 }
207204 for (var i = 0, len = prefix.length; i < len; i++) {
208139 if (path.charAt(i) !== prefix.charAt(i)) {
209139 return prefix.substring(0, i);
210 }
211 }
21265 return prefix;
213}
214
2151module.exports = {
216 mode: 'scripts',
217 priority: 50,
218
219 buildMap: buildMap,
220
221 resource: function(context, next, complete) {
222292 var config = context.config;
223
224292 if (context.resource['module-map']) {
22523 var buildModuleMap = function(context, callback) {
22618 module.exports.buildMap(context, function(err, map, prefix) {
22718 if (err) {
2280 callback(err);
229 } else {
23018 var moduleMap = config.attributes.moduleMap || 'module.exports.moduleMap';
23118 stripPrefix(map, prefix);
23218 loadModuleMap(map, moduleMap, function(err, data) {
23318 callback(err, data && {data: data, inputs: [{event: 'module-map'}], generated: true, noSeparator: true, ignoreWarnings: true});
234 });
235 }
236 });
237 };
23823 buildModuleMap.sourceFile = undefined;
23923 complete(undefined, buildModuleMap);
240 } else {
241269 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) {
32217 var resource = context.resource;
33
34217 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 {
47200 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) {
21200 next(function(err, ret) {
22200 if (err) {
230 return complete(err);
24 }
25
26 // Generate the router if we have the info for it
27200 var module = context.module;
28200 if (module.routes) {
2947 ret.unshift({ routes: module.routes });
30 }
31
32200 complete(undefined, ret);
33 });
34 },
35 resource: function(context, next, complete) {
36238 var resource = context.resource,
37 module = context.module.name;
38
39238 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 {
49217 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) {
28423 return (attr.scope && attr.scope.scope) || attr.scope;
29}
301function toObj(obj) {
3195 return _.isString(obj) ? {scope: obj} : obj;
32}
33
341function generator(string) {
35174 var ret = function(context, callback) { callback(undefined, {data: string, generated: true, noSeparator: true}); };
36100 ret.stringValue = string;
37100 ret.sourceFile = undefined;
38100 ret.ignoreWarnings = true;
39100 return ret;
40}
41
421var scopeTemplateDelimiter = /\{?\{\{yield\}\}\}?/;
43
441function ensureModuleTemplates(context, complete) {
4549 if (!context.configCache.moduleTemplate) {
4631 var template = context.config.attributes.scope && context.config.attributes.scope.template;
4731 if (!template) {
4819 template = __dirname + '/scope-module.handlebars';
49 }
50
5131 context.fileUtil.loadTemplate(template, scopeTemplateDelimiter, function(err, templates) {
5232 if (err) {
531 complete(err);
54 } else {
5531 context.configCache.moduleTemplate = {
56 start: templates[0],
57 end: templates[1]
58 };
5931 complete();
60 }
61 });
62 } else {
6318 complete();
64 }
65}
66
671function wrapResources(resources, context) {
6848 var cache = context.moduleCache;
6948 if (!cache.scopeName) {
7048 var app = context.config.attributes.application,
71 appName = app && app.name;
72
7348 if (context.config.isTopLevel(context.module)) {
7434 cache.isTopNamespace = true;
7534 cache.scopeName = context.module.topLevelName || appName || context.module.name;
76 } else {
7714 cache.scopeName = appName + "['" + context.module.name + "']";
78 }
7948 cache.appName = appName;
80 }
81
82 // Wrap the module content in a javascript module
8348 if (resources.length) {
8448 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
9148 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
10648 callSpec.unshift('this');
10748 if (cache.isTopNamespace) {
10834 internalVars.unshift(cache.scopeName);
109 } else {
11014 internalScope += cache.scopeName + ' = exports;';
111 }
11288 internalVars = _.map(internalVars, function(name) { return name + ' = exports'; });
11348 if (internalVars.length) {
11435 internalScope += 'var ' + internalVars.join(', ') + ';';
115 }
116
11748 var scopeDecl = '';
11848 if (context.moduleCache.isTopNamespace) {
119 // Insert the package declaration
12034 scopeDecl = 'var ' + context.moduleCache.scopeName + ';';
121 }
12248 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
13248 resources.unshift(generator(context.configCache.moduleTemplate.start(templateContext)));
13348 resources.push(generator(context.configCache.moduleTemplate.end(templateContext)));
134 }
13548 return resources;
136}
137
1381module.exports = {
139 mode: 'scripts',
140 priority: 50,
141
142 loadMixin: function(context, next, complete) {
14383 var mixinScope = toObj(context.loadedLibrary.scope);
14483 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 }
16983 next(complete);
170 },
171 loadConfig: function(context, next, complete) {
172131 var modules = context.config.attributes.modules;
173
174131 try {
175131 _.each(modules, function(module) {
176154 var mixins = context.libraries.moduleMixins(module);
177
178154 _.each(mixins, function(mixin) {
17946 context.libraries.mergeHash('aliases', module, mixin.library.attributes, module);
180 });
181 });
182 } catch (err) {
1830 return complete(err);
184 }
185
186131 next(complete);
187 },
188
189 resourceList: function(context, next, complete) {
190317 next(function(err, resources) {
191317 if (err) {
1920 return complete(err);
193 }
194
195317 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 }
201317 complete(undefined, resources);
202 });
203 },
204
205 module: function(context, next, complete) {
206106 next(function(err) {
207106 if (err) {
2080 return complete(err);
209 }
210
211106 var resources = context.moduleResources,
212 scope = getScope(context.config.attributes);
213
214106 if (resources.length && scope !== 'none') {
21549 ensureModuleTemplates(context, function(err) {
21650 if (err) {
2171 complete(err);
218 } else {
219 // Split up globals and non-globals
22049 var globals = [],
221 children = [],
222 moduleStart = [];
22349 for (var i = 0; i < resources.length; i++) {
224162 var resource = resources[i];
225162 if (resource.moduleStart) {
22613 moduleStart.push(resource);
227149 } else if (!resource.global) {
228133 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
23948 children = moduleStart.concat(children);
24048 globals.push.apply(globals, wrapResources(children, context, complete));
241
24248 context.moduleResources = globals;
24348 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

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

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

8%
23
2
21
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) {
80 return /\.(js|json)$/;
9 },
10
11 outputConfigs: function(context, next, complete) {
120 next(function(err, files) {
130 if (err) {
140 return complete(err);
15 }
16
17 // Permutation of other configs and ours
180 var ret = [];
190 files.forEach(function(fileConfig) {
200 [true, false].forEach(function(server) {
210 var config = _.clone(fileConfig);
220 config.server = server;
230 ret.push(config);
24 });
25 });
260 complete(undefined, ret);
27 });
28 },
29
30 fileName: function(context, next, complete) {
310 next(function(err, ret) {
320 if (ret && context.fileConfig.server) {
330 ret.path += '-server';
34 }
350 complete(err, ret);
36 });
37 },
38
39 moduleResources: function(context, next, complete) {
400 var module = context.module;
41
420 var files = [];
430 (module.server || module.scripts).forEach(function(script) {
440 if (!_.has(script, 'server') || script.server === context.fileConfig.server) {
450 files.push(script);
46 }
47 });
48
490 complete(undefined, files);
50 }
51};
52

/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) {
877 next(function(err) {
977 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) {
983 var mixinStyles = context.loadedLibrary.styles;
1083 if (mixinStyles) {
1120 var styles = context.libraries.originalConfig.styles || {},
12 configStyles = _.clone(context.config.attributes.styles || styles),
13 assigned = false;
14
1520 ['configObject'].forEach(function(key) {
1620 if ((key in mixinStyles) && !(key in styles)) {
171 configStyles[key] = mixinStyles[key];
18
191 assigned = true;
20 }
21 });
22
2320 if (context.libraries.mergeFiles('config', styles, mixinStyles, configStyles, context.loadedLibrary)) {
244 assigned = true;
25 }
26
2720 if (assigned) {
284 context.config.attributes.styles = configStyles;
29 }
30 }
3183 next(complete);
32 },
33
34 resource: function(context, next, complete) {
35429 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 {
70426 next(complete);
71 }
72 },
73
74 module: function(context, next, complete) {
75202 next(function() {
76202 var styles = context.config.attributes.styles || {},
77 config = styles.config || [];
78
79202 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
90202 complete();
91 });
92 }
93};
94

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

95%
127
121
6
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
29// Forward cache resets to any workers
301fu.on('cache:reset', function(path) {
31305 worker.sendAll({type: 'cache:reset', path: path});
32});
33
341var worker = new ChildPool(__dirname + '/stylus-worker');
35
361function generateSource(context, options, styleConfig) {
3737 var includes = (styleConfig.includes || []).concat(options.files),
38 module = options.module;
39
4037 var nibLocation = includes.indexOf('nib'),
41 useNib;
4237 if (styleConfig.useNib) {
4331 useNib = true;
4431 includes.unshift('nib');
456 } else if (nibLocation >= 0) {
46 // Special case nib handling to maintain backwards compatibility
47 // WARN: This may be deprecated in future releases
480 useNib = true;
490 includes.splice(nibLocation, 1);
50 }
51
5237 var declare = context.config.platformList().map(function(platform) {
5372 return '$' + platform + ' = ' + (platform === context.platform);
54 }).join('\n') + '\n';
55
5637 var mixins = [],
57 mixinLUT = {};
58
5937 var source = declare + includes.map(function(include) {
60120 var source = include.library;
61120 var statement = '@import ("' + (include.originalSrc || include.src || include) + '")\n';
62120 if (source) {
6313 var name = '',
64 root = (source.parent || source).root || '',
65 stylusRoot = ((source.parent || source).styles || {}).styleRoot,
66 library = (source.parent || source).name || '';
6713 if (source.parent) {
6811 name = source.name || '';
69 }
7013 var mixinName = name + '_' + library;
71
7213 if (!mixinLUT[mixinName]) {
737 var mixinDecl = context.libraries.findDecl(module.mixins, {name: name, library: library}),
74 overrides = mixinDecl && mixinDecl.overrides;
757 var overrideLibrary;
767 if (source.overrideLibrary) {
771 overrideLibrary = {
78 root: normalize(source.overrideLibrary.root),
79 stylusRoot: (source.overrideLibrary.styles || {}).styleRoot
80 };
81 }
82
837 mixins.push({
84 root: normalize(root),
85 stylusRoot: stylusRoot && normalize(stylusRoot),
86 overrides: source.overrides,
87 overrideLibrary: overrideLibrary
88 });
897 mixinLUT[mixinName] = mixins.length-1;
90 }
9113 mixinName = mixinLUT[mixinName];
92
9313 return 'push-mixin("' + mixinName + '")\n'
94 + statement
95 + 'pop-mixin()\n';
96 } else {
97107 return statement;
98 }
99 }).join('');
100
10137 return {
102 useNib: useNib,
103 source: source,
104 mixins: mixins
105 };
106}
107
1081function compile(options, callback) {
10937 var context = options.context,
110
111 styleConfig = context.config.attributes.styles || {};
112
11337 var loadPrefix = context.config.loadPrefix(),
114 externalPrefix;
11537 if (loadPrefix) {
1169 externalPrefix = loadPrefix + (context.buildPath.indexOf('/') >= 0 ? path.dirname(context.buildPath) + '/' : '');
117 }
118
11937 var imageOptions = {
120 outdir: path.dirname(context.fileName),
121 resolutions: context.modeCache.pixelDensity,
122 limit: styleConfig.urlSizeLimit,
123 copyFiles: styleConfig.copyFiles,
124 externalPrefix: externalPrefix
125 };
126
12737 var source = generateSource(context, options, styleConfig);
128
12937 context.fileUtil.ensureDirs(context.fileName, function(err) {
13037 if (err) {
1310 return callback(err);
132 }
133
13437 worker.send({
135 plugins: options.plugins,
136
137 useNib: source.useNib,
138 imageOptions: imageOptions,
139
140 filename: options.filename,
141 minimize: context.options.minimize,
142
143 source: source.source,
144 mixins: source.mixins,
145
146 lookupPath: context.fileUtil.lookupPath(),
147 styleRoot: styleConfig.styleRoot && context.fileUtil.resolvePath(styleConfig.styleRoot)
148 },
149 callback);
150 });
151}
152
1531module.exports = {
154 // scripts mode is used also to support inline styles
155 mode: ['styles', 'scripts'],
156 priority: 50,
157
158 loadMixin: function(context, next, complete) {
15983 var mixinStyles = context.loadedLibrary.styles;
16083 if (mixinStyles) {
16120 var styles = context.libraries.originalConfig.styles || {},
162 configStyles = _.clone(context.config.attributes.styles || styles),
163 assigned = false;
164
16520 ['urlSizeLimit', 'copyFiles', 'useNib'].forEach(function(key) {
16660 if ((key in mixinStyles) && !(key in styles)) {
1678 configStyles[key] = mixinStyles[key];
168
1698 assigned = true;
170 }
171 });
172
17320 if (context.libraries.mergeFiles('includes', styles, mixinStyles, configStyles, context.loadedLibrary)) {
1745 assigned = true;
175 }
176
17720 if (context.libraries.mergeHash('pixelDensity', styles, mixinStyles, configStyles)) {
1783 assigned = true;
179 }
180
18120 if (assigned) {
1829 context.config.attributes.styles = configStyles;
183 }
184 }
18583 next(complete);
186 },
187
188 outputConfigs: function(context, next, complete) {
189195 if (!inlineStyles.isInline(context) && context.mode !== 'styles') {
19094 return next(complete);
191 }
192
193101 next(function(err, files) {
194101 if (err) {
1950 return complete(err);
196 }
197
198101 var ret = [],
199 styleConfig = context.config.attributes.styles || {},
200 pixelDensity = styleConfig.pixelDensity || {};
201101 if (context.platform) {
20274 pixelDensity = pixelDensity[context.platform] || pixelDensity;
203 }
204101 if (!_.isArray(pixelDensity)) {
20564 pixelDensity = [ 1 ];
206 }
207101 context.modeCache.pixelDensity = pixelDensity;
208
209 // Permutation of other configs and ours
210101 var primary = true;
211101 files.forEach(function(fileConfig) {
212101 pixelDensity.forEach(function(density) {
213139 var config = _.clone(fileConfig);
214139 config.pixelDensity = density;
215139 config.isPrimary = primary;
216139 primary = false;
217139 ret.push(config);
218 });
219 });
220101 complete(undefined, ret);
221 });
222 },
223
224 fileName: function(context, next, complete) {
225233 if (!inlineStyles.isInline(context) && context.mode !== 'styles') {
226105 return next(complete);
227 }
228
229128 next(function(err, ret) {
230128 if (ret && context.fileConfig.pixelDensity !== 1) {
23140 ret.path += '@' + context.fileConfig.pixelDensity + 'x';
232 }
233128 complete(err, ret);
234 });
235 },
236
237 module: function(moduleContext, next, complete) {
238205 next(function(err) {
239 /*jshint eqnull: true */
240205 if (err) {
2410 return complete(err);
242 }
243
244205 function mergeResources(start) {
24555 var generator = function(context, callback) {
24655 function response(data, density) {
24755 if (data) {
24852 return {
249 data: data.data[density || 1],
250 inputs: data.inputs,
251 noSeparator: true
252 };
253 }
254 }
255
25655 var filename = generator.filename;
257
258 // We only want to call stylus once which will generate the css for all of the
259 // resolutions we support on this platform. This ugly bit of code make sure that
260 // we properly handle all of that loading states that can come into play under these
261 // circumstances while still adhering to the output models prescribed by lumbar.
26255 var queue = context.modeCache['stylus_' + filename];
26355 if (_.isArray(queue)) {
264 // We are currently executing
26518 queue.push({density: context.fileConfig.pixelDensity, callback: callback});
26637 } else if (_.isObject(queue)) {
267 // We already have data
2680 callback(undefined, response(queue, context.fileConfig.pixelDensity));
269 } else {
270 // We need to kick of a stylus build
27137 queue = context.modeCache['stylus_' + filename] = [
272 {density: context.fileConfig.pixelDensity, callback: callback}
273 ];
27437 var options = {
275 filename: filename,
276 files: generator.inputs,
277
278 context: context,
279 module: moduleContext.module, // To play nicely with combined mode
280 plugins: generator.plugins
281 };
28237 compile(options, function(err, data) {
28337 if (err) {
2843 data = undefined;
285 }
28637 _.each(queue, function(callback) {
28755 callback.callback(err, response(data, callback.density));
288 });
28937 context.modeCache['stylus_' + filename] = data;
290 });
291 }
292 };
29355 generator.inputs = resources.splice(start, rangeEnd - start + 1);
294150 generator.filename = 'stylus_' + _.map(generator.inputs, function(file) { return file.originalSrc || file.src; }).join(';');
29555 generator.style = true;
29655 generator.stylus = true;
29755 generator.plugins = [];
298
29955 resources.splice(start, 0, generator);
30055 rangeEnd = undefined;
301 }
302
303 // Merge all consequtive stylus files together
304205 var resources = moduleContext.moduleResources,
305 len = resources.length,
306 rangeEnd;
307205 while (len--) {
308430 var resource = resources[len];
309
310430 if (/\.styl$/.test(resource.src)) {
31195 if (!rangeEnd) {
31255 rangeEnd = len;
313 }
314335 } else if (rangeEnd) {
3153 mergeResources(len + 1);
316 }
317 }
318205 if (rangeEnd != null) {
31952 mergeResources(0);
320 }
321205 complete();
322 });
323 }
324};
325

/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) {
3183 var mixinTemplates = context.loadedLibrary.templates;
3283 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 }
4583 next(complete);
46 },
47
48 resourceList: function(context, next, complete) {
49317 var library = context.resource.library,
50 attr = (library && library.parent || library) || context.config.attributes;
51
52317 next(function(err, ret) {
53317 if (err || !ret) {
540 return complete(err);
55 }
56
57317 function pushTemplates(templates) {
58284 _.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
74317 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) || '';
78317 if (_.isString(resource) && resource.indexOf(mixinRoot) === 0) {
79241 resource = resource.substring(mixinRoot.length);
80 }
81
82317 var deferComplete;
83317 if (build.filterResource(context.resource, context)) {
84273 pushTemplates(views[resource]);
85
86273 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 }
123317 if (!deferComplete) {
124306 complete(undefined, ret);
125 }
126 });
127 },
128
129 resource: function(context, next, complete) {
130200 var resource = context.resource;
131
132200 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 {
147198 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 pathRelative = require('../path-relative'); // Shim for 0.4 support
8
91module.exports = {
10 mode: 'static',
11 priority: 50,
12
13 updateHtmlReferences: function(context, content, callback) {
1415 function updateResources(mode, query, create) {
1530 return function(callback) {
1629 async.forEach($(query), function(el, callback) {
1710 el = $(el);
1810 var module = (el.attr('src') || el.attr('href')).replace(/^module:/, '');
1910 context.fileNamesForModule(mode, module, function(err, fileNames) {
2010 if (err) {
212 return callback(err);
22 }
23
24 // Generate replacement elements for each of the entries
258 var content = fileNames.map(function(fileName) {
268 if (fileName.server) {
270 return '';
28 }
298 return create(loadDirName + basename(fileName.fileName.path) + '.' + fileName.fileName.extension);
30 });
31
32 // Output and kill the original
338 el.replaceWith(content.join(''));
34
358 callback();
36 });
37 },
38 callback);
39 };
40 }
4115 var $ = cheerio.load(content),
42 loadDirName = '';
4315 async.series([
44 function(callback) {
45 // Output the load prefix script we we have a module: script reference
4615 var firstScript = $('script[src^="module:"]');
4715 if (firstScript) {
4815 context.plugins.get('module-map').buildMap(context, function(err, map, loadPrefix) {
4915 if (err) {
500 return callback(err);
51 }
52
5315 var noFileComponent = !loadPrefix;
5415 loadPrefix = context.platformPath + loadPrefix;
5515 var dirname = path.dirname(loadPrefix + 'a'); // Force a component for the trailing '/' case
56
57 // Only remap load prefix if not defined by the user
5815 if (!(loadDirName = context.config.loadPrefix())) {
5914 var resourcePath = path.dirname(context.fileName.substring(context.outdir.length + 1));
6014 loadPrefix = pathRelative.relative(resourcePath, loadPrefix);
6114 loadDirName = pathRelative.relative(resourcePath, dirname);
62
6314 if (loadDirName) {
647 loadDirName += '/';
65 }
6614 if (loadPrefix && noFileComponent) {
677 loadPrefix += '/';
68 }
69 } else {
70 // A load prefix was given, just combine this with the module map prefix
711 loadPrefix = loadDirName + loadPrefix;
721 if (dirname !== '.') {
731 loadDirName += dirname + '/';
74 }
75 }
76
7715 var script = '<script type="text/javascript">var lumbarLoadPrefix = \'' + loadPrefix + '\';</script>';
7815 firstScript.before(script);
7915 callback();
80 });
81 } else {
820 callback();
83 }
84 },
85 updateResources('scripts', 'script[src^="module:"]', function(href) {
867 return '<script type="text/javascript" src="' + href + '"></script>';
87 }),
88 updateResources('styles', 'link[href^="module:"]', function(href) {
891 return '<link rel="stylesheet" type="text/css" href="' + href + '"/>';
90 })
91 ],
92 function(err) {
9315 callback(err, $.html());
94 });
95 },
96
97 resource: function(context, next, complete) {
9828 var resource = context.resource;
9928 if (resource['update-externals'] || (/\.html?$/.test(resource.src) && resource['update-externals'] !== false)) {
1006 next(function(err, resource) {
1016 function generator(context, callback) {
102 // Load the source data
1036 context.loadResource(resource, function(err, file) {
1046 if (err) {
1050 return callback(err);
106 }
107
108 // Update the content
1096 module.exports.updateHtmlReferences(context, file.content, function(err, data) {
1106 callback(err, {
111 data: data,
112 inputs: file.inputs
113 });
114 });
115 });
116 }
117
118 // Include any attributes that may have been defined on the base entry
1196 if (!_.isString(resource)) {
1206 _.extend(generator, resource);
121 }
1226 complete(undefined, generator);
123 });
124 } else {
12522 next(complete);
126 }
127 }
128};
129

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

100%
126
126
0
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 build = require('./build'),
4 configLoader = require('./config'),
5 Context = require('./context'),
6 fs = require('fs'),
7 fu = require('./fileUtil'),
8 Libraries = require('./libraries'),
9 plugin = require('./plugin');
10
111exports.loadConfig = function(path, event, options, callback) {
1238 try {
1338 fu.resetCache();
14
1538 var config = _.isString(path) ? configLoader.load(path) : configLoader.create(path);
16
1738 var plugins = plugin.create(options);
1838 plugins.initialize(config);
19
2037 config.outdir = options.outdir = options.outdir || config.attributes.output;
21
2237 var libraries = new Libraries(options);
2337 var context = new Context(options, config, plugins, libraries, event);
2437 context.options = options;
2537 context.configCache = {};
26
2737 libraries.initialize(context, function(err) {
2837 if (err) {
291 return callback(err);
30 }
31
3236 plugins.loadConfig(context, function(err) {
3336 if (err) {
341 return callback(err);
35 }
36
3735 event.emit('config', context.config);
3835 if (options.verbose) {
391 event.emit('log', 'Finalized config ' + JSON.stringify(context.config.serialize(), undefined, 2));
40 }
41
42 // Ensure that we have the proper build output
4335 if (!config.outdir) {
441 return callback(new Error('Output must be defined on the command line or config file.'));
45 }
4634 context.outdir = config.outdir;
47
4834 fu.ensureDirs(config.outdir + '/.', function() {
4934 var stat = fs.statSync(config.outdir);
5034 if (!stat.isDirectory()) {
511 callback(new Error('Output must be a directory'));
52 } else {
5333 callback(undefined, context);
54 }
55 });
56 });
57 });
58 } catch (err) {
591 callback(err);
60 }
61};
62
631exports.buildPackages = function(context, packageName, modules, callback) {
6440 if (!callback) {
6514 callback = modules;
6614 modules = undefined;
67 }
68
69 // Allow a string or a list as modules input
7040 if (!_.isArray(modules)) {
7138 modules = [modules];
722 } else if (!modules.length) {
73 // Special case empty array input to build all
741 modules = [undefined];
75 }
76
7740 var options = {};
7840 if (typeof packageName === 'object') {
791 options = packageName;
801 packageName = options.package;
81 }
82
8340 var packageNames = packageName ? [packageName] : context.config.packageList(),
84 contexts = [];
85
8640 packageNames.forEach(function(pkg) {
8744 modules.forEach(function(module) {
8845 options.package = pkg;
8945 options.module = module || undefined; // '' -> undefined
90
9145 context.event.emit('debug', 'Build package: ' + pkg);
92
9345 var platforms = context.config.platformList(pkg);
9445 platforms.forEach(function(platform) {
9561 options.platform = platform;
96
9761 var newContext = context.clone(options);
9861 contexts.push(newContext);
99 });
100 });
101 });
102
10340 async.forEach(contexts, exports.buildPlatform, callback);
104};
1051exports.buildPlatform = function(context, callback) {
10690 context.event.emit('debug', 'Build platform: ' + context.description);
10790 var modes = context.mode ? [context.mode] : context.plugins.modes();
108
10990 context.platformCache = context.platformCache || {};
110
111 // If in AMD mode then run scripts modes sync before others. This ensures that
112 // any scripts the define style or script dependencies will be processed prior
113 // to the dependency module's output.
11490 if (context.config.attributes.amd && _.contains(modes, 'scripts')) {
1152 modes = _.without(modes, 'scripts');
1162 exports.buildMode('scripts', context, function(err) {
1172 if (err) {
1181 return callback(err);
119 }
120
1211 execModes();
122 });
123 } else {
12488 if (context.config.attributes.amd && !context.platformCache.amdAppModules) {
1251 return callback(new Error('Attempting to build mode "' + context.mode + '" without AMD config'));
126 }
127
12887 execModes();
129 }
130
13189 function execModes() {
13288 async.forEach(modes, function(mode, callback) {
133187 exports.buildMode(mode, context, callback);
134 },
135 callback);
136 }
137};
1381exports.buildMode = function(mode, context, callback) {
139188 context.event.emit('debug', 'Build mode: ' + mode + ' ' + context.description);
140
141188 var modules = context.module ? [context.module] : context.config.moduleList(context.package);
142
143188 if (context.config.attributes.amd) {
1441 var topLevelNames = modules.filter(function(module) {
1452 return context.config.isTopLevel(module);
146 });
1471 modules = _.difference(modules, topLevelNames);
148 }
149
150188 context = context.clone();
151188 context.mode = mode;
152188 context.modeCache = {};
153
154188 if (context.fileConfig) {
15524 processFileConfig(context.fileConfig, callback);
156 } else {
157164 context.plugins.outputConfigs(context, function(err, configs) {
158164 if (err) {
1591 return callback(err);
160 }
161163 async.forEach(configs, processFileConfig, callback);
162 });
163 }
164
165188 function processFileConfig(fileConfig, callback) {
166200 var fileContext = context.clone(true);
167200 fileContext.fileConfig = fileConfig;
168200 fileContext.resources = [];
169200 fileContext.combineResources = {};
170200 fileContext.fileCache = fileContext.combined ? {} : undefined;
171
172200 async.eachSeries([
173 topLevelNames,
174 modules
175 ],
176 function(modules, callback) {
177400 if (!modules) {
178198 return callback();
179 }
180
181202 async.forEach(modules, function(module, callback) {
182291 var moduleContext = fileContext.clone();
183291 moduleContext.module = module;
184
185291 exports.buildModule(moduleContext, callback);
186 },
187 callback);
188 },
189 function(err) {
190196 if (err) {
1915 return callback(err);
192 }
193
194191 context.plugins.modeComplete(fileContext, callback);
195 });
196 }
197};
1981exports.buildModule = function(context, callback) {
199282 context.event.emit('debug', 'Build module: ' + context.description);
200
201282 var module = context.config.module(context.module);
202282 if (!module) {
2031 return callback(new Error('Unable to find module "' + context.module + '"'));
204 }
205
206281 if (context.config.attributes.amd
207 && !context.platformCache.amdAppModules
208 && !context.config.isTopLevel(module)) {
2091 return callback(new Error('Attempting to build module "' + context.module + '" without AMD config'));
210 }
211
212280 context.module = module;
213280 context.fileCache = context.combined ? context.fileCache : {};
214280 context.moduleCache = {};
215
216280 var resource = context.resource;
217280 if (resource) {
2187 resource = resource.originalResource || resource;
2197 exports.processResources(context, [resource], callback);
220 } else {
221 // Load all resources associated with this module
222273 build.loadResources(context, function(err, resources) {
223271 if (err) {
2241 return callback(err);
225 }
226270 exports.processResources(context, resources, callback);
227 });
228 }
229};
230
2311exports.processResources = function(context, resources, callback) {
232277 build.processResources(resources, context, function(err, resources) {
233277 if (err) {
2341 return callback(err);
235 }
236
237276 context.moduleResources = resources;
238276 context.plugins.module(context, callback);
239 });
240};
241

/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
1679 return string.replace(ESCAPER, function(c) { return ESCAPER_LUT[c] || c; });
17};
18

/Users/kpdecker/dev/walmart/lumbar/lib/util/define-parser.js

98%
73
72
1
LineHitsSource
11var esprima = require('esprima'),
2 traverse = require('estraverse').traverse;
3
41var sourceMap = require('source-map'),
5 SourceMapConsumer = sourceMap.SourceMapConsumer,
6 SourceMapGenerator = sourceMap.SourceMapGenerator;
7
81function expect(node, type, hint) {
9229 node = node || {};
10
11229 if (node.type !== type) {
122 throw new Error((hint || 'Expected ') + '"' + type + '" but found "' + node.type + '"' + errorLocation(node));
13 }
14}
151function errorLocation(node) {
166 return ' location: line: ' + node.loc.start.line + ' column: ' + node.loc.start.column;
17}
18
191function parseDefine(node, allowConfig) {
2083 var args = node.arguments,
21 name,
22 deps,
23 functionArg;
24
2583 if (args[0].type === 'FunctionExpression') {
268 functionArg = args[0];
2775 } else if (args[0].type === 'ArrayExpression') {
2856 deps = args[0];
2956 functionArg = args[1];
3019 } else if (args[0].type === 'Literal') {
3118 name = args[0].value;
3218 if (args.length > 2) {
3311 deps = args[1];
3411 functionArg = args[2];
35 } else {
367 functionArg = args[1];
37 }
38 } else {
391 expect(args[0], 'Literal', 'Expected name to be ');
40 }
41
4282 if (deps) {
4367 expect(deps, 'ArrayExpression', 'Expected dependencies to be ');
4467 deps.elements.forEach(function(arg) {
4565 expect(arg, 'Literal', 'Expected dependency to be ');
46 });
47
48130 deps = deps.elements.map(function(arg) { return arg.value; });
49 }
50
5181 expect(functionArg, 'FunctionExpression');
5281 return {
53 define: true,
54 name: name,
55 deps: deps,
56 range: functionArg.range,
57 loc: functionArg.loc
58 };
59}
601var workers = {
61 define: function(node) {
6227 return parseDefine(node);
63 },
64 defineView: function(node) {
6544 var ret = parseDefine(node);
6644 ret.view = true;
6744 return ret;
68 },
69 defineHelper: function(node) {
705 var ret = parseDefine(node);
715 ret.helper = true;
725 return ret;
73 },
74 defineRouter: function(node) {
75 // Pull the first node off for the routes. Note that this creates an odd structure for
76 // explicitly named routers but we're ok with that as that case should be uncommon and the
77 // code is much simplier via this route.
787 var routesNode = node.arguments[0],
79 routes = {};
807 expect(routesNode, 'ObjectExpression', 'Expected first argument to be');
81
827 routesNode.properties.forEach(function(prop) {
838 if (prop.key.type !== 'Identifier' && prop.key.type !== 'Literal') {
840 throw new Error('Expected route to be "Identifier" or "Literal" but found "' + prop.key.type + '"' + errorLocation(prop));
85 }
868 expect(prop.value, 'Literal', 'Expected route handler to be');
87
888 routes[prop.key.name || prop.key.value] = prop.value.value;
89 });
90
917 node.arguments.shift();
92
937 var ret = parseDefine(node);
947 ret.router = true;
957 ret.routes = routes;
967 return ret;
97 }
98};
99
1001module.exports = function(src, options) {
10182 options = options || {};
102
10382 var ast = esprima.parse(src, {range: true, loc: true}),
104 defineInfo,
105 defineNode,
106 rangeStart = 0,
107 locStart = {line: 1, column: 0},
108 locEnd,
109 ret = [];
110
11180 traverse(ast, {
112 enter: function(node, parent) {
113674 if (node.type === 'CallExpression') {
11483 var worker = workers[node.callee.name];
11583 if (worker) {
11683 defineInfo = worker(node);
117
11881 if (defineNode) {
1194 throw new Error('Unsupported nested define "' + defineInfo.name + '" found' + errorLocation(node));
120 }
12177 defineNode = node;
122
123
12477 defineInfo.source = src.slice.apply(src, defineInfo.range);
12577 delete defineInfo.range;
126
12777 if (rangeStart !== node.range[0]) {
1283 ret.push({
129 source: src.slice(rangeStart, node.range[0]),
130 loc: {
131 start: locStart,
132 end: {line: node.loc.start.line, column: Math.max(node.loc.start.column-1, 0)}
133 }
134 });
135 }
136 }
137 }
138 },
139 leave: function(node, parent) {
140640 if (node === defineNode) {
14173 ret.push(defineInfo);
142
14373 rangeStart = defineNode.range[1] + 1;
14473 locStart = {line: node.loc.end.line, column: node.loc.end.column + 1};
145
14673 defineNode = defineInfo = undefined;
147 }
148640 locEnd = node.loc.end;
149 }
150 });
151
152 // Handle trailing source
15374 if (rangeStart < src.length) {
1546 ret.push({
155 source: src.slice(rangeStart),
156 loc: {
157 start: locStart,
158 end: locEnd
159 }
160 });
161 }
162
16374 return ret;
164};
165

/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) {
23123 this.output = output;
24123 if (SourceMapGenerator) {
25123 this.generator = new SourceMapGenerator({file: output});
26 }
27
28123 this.contentCache = {};
29123 this.line = 1;
30123 this.column = 1;
31123 this._content = '';
32};
33
341exports.prototype.add = function(name, content, context, generated) {
35517 this._sourceMap = '';
36517 this._consumer = undefined;
37
38517 var lines = content.split('\n');
39517 if (name && !generated) {
40191 this.contentCache[name] = {
41 lines: lines,
42 context: context
43 };
44 }
45
46517 if (this.generator) {
47517 _.each(lines, function(line, index) {
482878 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
62517 this.line += lines.length - 1;
63517 if (lines.length >= 2) {
64486 this.column = 1;
65 }
66517 this.column += lines[lines.length - 1].length;
67
68517 this._content += content;
69};
701exports.prototype.content = function() {
71279 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

97%
49
48
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) {
11825 if (_.isString(resource)) {
12270 return {src: resource};
13 } else {
14555 return resource;
15 }
16 },
17
18 source: function(resource) {
19972 return resource.src || resource.dir || resource;
20 },
21
22 map: function(resource, library, config) {
23392 var bowerPath;
24392 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
29392 if (!library && !bowerPath) {
30281 return resource;
31 }
32
33111 resource = resources.cast(_.clone(resource));
34
35111 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
39111 _.extend(resource, _.omit(config, 'overrideLibrary'));
40
41111 if (_.isString(src)) {
42109 var mixinOverride = library && library.overrides && library.overrides[src],
43 libraryOverride = library && library.parent && library.parent.overrides && library.parent.overrides[src],
44 override = (mixinOverride || mixinOverride === false) ? mixinOverride : libraryOverride,
45
46 librarySrc = bowerPath || library.root || '';
47109 librarySrc = librarySrc ? path.join(librarySrc, src) : src;
48
49109 if (override) {
5017 resource.originalSrc = librarySrc;
5117 librarySrc = _.isString(override) ? override : src;
52
5317 if (library.overrideLibrary) {
546 librarySrc = path.join(library.overrideLibrary.root || '', librarySrc);
55 }
5692 } else if (override === false) {
573 return;
58 }
59
60106 if (resource.src) {
61105 resource.src = librarySrc;
621 } else if (resource.dir) {
631 resource.dir = librarySrc;
64 }
65 }
66
67108 resource.library = library;
68108 return resource;
69 },
70
71 relativePath: function(src, library) {
7239 if (src.indexOf('./') === 0) {
730 src = src.substring(2);
74 }
7539 src = normalize(src);
76
7739 if (!library) {
7836 return src;
79 }
80
81 // Attempt to strip either the root of the base or overriding library as we don't know
82 // which we might be
833 var mixinRoot = library.root || '';
843 if (src.indexOf(mixinRoot) === 0) {
852 src = src.substring(mixinRoot.length);
861 } else if (library.overrideLibrary) {
871 mixinRoot = library.overrideLibrary.root || '';
88
891 if (src.indexOf(mixinRoot) === 0) {
901 src = src.substring(mixinRoot.length);
91 }
92 }
933 return src;
94 },
95
96 pathToLibrary: function(src, library) {
9739 src = resources.relativePath(src, library);
98
9939 var overrides = library && library.overrides;
10039 if (overrides) {
1011 overrides = _.invert(overrides);
102
103 // Warn not supporting directories at this point in time. Matches must be 1 to 1
1041 return overrides[src] || src;
105 }
106
10738 return src;
108 }
109};
110

/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) {
45187 var watchInfo = {
46 callback: callback,
47 parents: [],
48 queue: []
49 };
50187 if (parent) {
51106 watchInfo.parents.push(parent);
52 }
53187 watchedFiles[filename.virtual || filename] = watchInfo;
54
55187 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) {
123103 var watch = watchedFiles[filename.virtual || filename];
124103 if (!watch) {
125 // Create a watch on this and all others
12681 watchFile(filename, callback);
127 } else {
12822 watch.callback = callback;
129 }
130
131103 filename = filename.virtual || filename;
132
133103 dependencies.forEach(function(depend) {
134275 var watch = watchedFiles[depend.virtual || depend];
135275 if (!watch) {
136106 watchFile(depend, undefined, filename);
137 } else {
138169 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() {
18862 _.each(watchedFiles, function(watch, name) {
189182 unwatch(name);
190 });
191};
192
1931function unwatch(name) {
194187 watchedFiles[name].callback = undefined;
195187 if (watchedFiles[name].watch) {
1966 watchedFiles[name].watch.close();
197 }
198187 delete watchedFiles[name];
199}
200

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

96%
50
48
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(eventBus) {
830 EventEmitter.call(this);
9
1030 this.eventBus = eventBus;
1130 this.reset();
12
1330 this._exec = this.setupExec();
14}
15
161WatchManager.prototype = {
17 configFile: function(path, mixins, callback) {
1819 if (_.isFunction(mixins)) {
190 callback = mixins;
200 mixins = undefined;
21 }
22
2319 var self = this;
2419 watcher.watchFile(path, mixins || [], function() {
254 self.emit('watch-change', {fileName: path, config: true});
26
274 self.pushChange({callback: callback, fileName: path, config: true});
28 });
29 },
30 moduleOutput: function(status, callback) {
3177 var self = this;
32
3377 function theWatcher(type, filename, sourceChange) {
3440 self.emit('watch-change', {fileName: sourceChange, output: status.fileName});
3540 self.pushChange({
36 callback: callback,
37 type: type,
38 fileName: status.fileName,
39 sourceChange: sourceChange
40 });
41 }
42
4377 var watchInfo = this.watching[status.fileName] || {},
44
45280 eventInputs = status.inputs.filter(function(input) { return input.event; }),
46268 fileInputs = _.difference(status.inputs, eventInputs).map(function(input) { return fu.resolvePath(input.dir || input); }),
47
48 removedFiles = _.difference(watchInfo.files, fileInputs);
49
5077 if (removedFiles.length) {
512 watcher.unwatch(status.fileName, removedFiles);
52 }
5377 watcher.watchFile({ virtual: status.fileName }, fileInputs, theWatcher);
54
55 // Remove all events as we will reregister below
5677 _.each(watchInfo.events, function(event) {
575 this.eventBus.removeListener(event, watchInfo.watcher);
58 }, this);
59
6077 eventInputs = _.pluck(eventInputs, 'event');
6177 _.each(eventInputs, function(event) {
6212 this.eventBus.on(event, theWatcher);
63 }, this);
64
6577 this.watching[status.fileName] = {
66 watcher: theWatcher,
67 files: fileInputs,
68 events: eventInputs
69 };
70 },
71
72
73 setupExec: function() {
7411 return _.debounce(_.bind(this.flushQueue, this), 500);
75 },
76 flushQueue: function() {
7725 if (this.queue.length) {
7825 _.each(this.queue, function(change) {
7942 change.callback();
80 });
8125 this.queue = [];
82 }
83 },
84
85 reset: function() {
86 // Cleanup what we can, breaking things along the way
87 // WARN: This prevents concurrent execution within the same process.
8854 watcher.unwatchAll();
89
9054 this.watching = {};
9154 this.queue = [];
92 },
93 pushChange: function(change) {
9461 fu.resetCache(change.sourceChange);
9561 if (change.type === 'remove' && change.sourceChange) {
963 fu.resetCache(path.dirname(change.sourceChange));
97 }
98
9961 if (_.find(this.queue, function(existing) {
10044 return existing.config || (change.fileName && (existing.fileName === change.fileName));
101 })) {
102 // If we have a pending config change or changes to the same file that has not started then
103 // we can ignore subsequent changes
1047 return;
105 }
106
10754 if (change.config) {
10810 this.reset();
109 }
110
11154 this.queue.push(change);
11254 this._exec();
113 }
114};
115
1161WatchManager.prototype.__proto__ = EventEmitter.prototype;
117
1181exports = module.exports = WatchManager;
119
Done, without errors.