Running "mochacov:cov" (mochacov) task Coverage

Coverage

91%
2083
1915
168

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

100%
47
47
0
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 fu = require('./fileUtil');
4
5/**
6 * Creates a list of all of the resources for the current module.
7 *
8 * Context state: module
9 *
10 * Plugin Calls:
11 * moduleResources
12 * fileFilter
13 * resourceList
14 */
151exports.loadResources = function(context, callback) {
16325 var plugins = context.plugins;
17
18325 function filterResource(resource) {
19546 if (_.isString(resource)) {
20162 resource = { src: resource };
21 }
22
23546 if (exports.filterResource(resource, context)) {
24465 return resource;
25 }
26 }
27
28325 plugins.moduleResources(context, function(err, files) {
29325 if (err) {
301 return callback(err);
31 }
32
33324 var fileFilter = plugins.fileFilter(context) || /.*/;
34324 fu.fileList(files, fileFilter, function(err, files) {
35324 if (err) {
361 return callback(err);
37 }
38
39323 async.map(files, function(resource, callback) {
40496 var resourceContext = context.clone();
41496 resourceContext.resource = resource;
42496 plugins.resourceList(resourceContext, callback);
43 },
44 function(err, resources) {
45323 resources = _.flatten(resources);
46323 resources = _.map(resources, filterResource);
47869 resources = _.filter(resources, function(resource) { return resource; });
48323 callback(err, resources);
49 });
50 });
51 });
52};
53
54/**
55 * Filters a given resource for platform constraints, if specified.
56 */
571exports.filterResource = function(resource, context) {
58889 function check(value, singular, plural) {
592419 if (typeof singular !== 'undefined') {
60172 return singular.not ? singular.not !== value : singular === value;
612247 } else if (plural) {
6273 var ret = (plural.not || plural).reduce(function(found, filePlatform) {
63105 return found || filePlatform === value;
64 }, false);
6573 return plural.not ? !ret : ret;
66 }
672174 return true;
68 }
69
70889 function checkResource(resource) {
71893 return check(context.platform, resource.platform, resource.platforms)
72 && check(context.package, resource.package, resource.packages)
73 && check(!!context.combined, resource.combined);
74 }
75889 return checkResource(resource)
76 && (!resource.originalResource || checkResource(resource.originalResource));
77};
78
79
80/**
81 * Runs a set of resources through the resource plugin.
82 *
83 * Context state: module
84 *
85 * Plugin Calls:
86 * resource
87 */
881exports.processResources = function(resources, context, callback) {
89324 var plugins = context.plugins;
90
91324 async.map(resources, function(resource, callback) {
92440 var resourceContext = context.clone();
93440 resourceContext.resource = resource;
94440 plugins.resource(resourceContext, function(err, newResource) {
95440 if (newResource && newResource !== resource) {
9698 newResource.originalResource = resource;
97 }
98
99440 callback(err, newResource);
100 });
101 },
102 function(err, resources) {
103324 if (err) {
1041 return callback(err);
105 }
106
107762 callback(err, resources.filter(function(resource) { return resource; }));
108 });
109};
110

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

85%
67
57
10
LineHitsSource
11var _ = require('underscore'),
2 fu = require('./fileUtil'),
3 path = require('path'),
4 vm = require('vm');
5
6/**
7 * Reads the RAW JSON for a lumbar config file.
8 */
91exports.readConfig = function(lumbarFile) {
1037 try {
1137 var data = '(' + fu.readFileSync(lumbarFile) + ')';
12
13 // Yes this is totally unsafe, but we don't want the strictness of pure JSON for our
14 // config files and if you are running an untrusted lumbar file you already have concerns.
1537 return vm.runInThisContext(data, lumbarFile);
16 } catch (err) {
170 var line;
180 try {
190 var esprima = require('esprima');
200 console.log(err.stack, esprima.parse(data));
21 } catch (err) {
220 if (err.lineNumber) {
230 line = ':' + err.lineNumber;
24 }
25 }
260 throw new Error('Failed to load config ' + lumbarFile + line + ': ' + err);
27 }
28};
29
30/**
31 *
32 * @name load
33 * @function This function loads the lumbar JSON file, and returns
34 * helper methods associated with accessing its specific data.
35 * @param {string} lumbarFile the location of the lumbar file.
36 * @return {Object}
37 */
381exports.load = function(lumbarFile) {
3929 fu.lookupPath('');
40
4129 var config = exports.readConfig(lumbarFile);
4229 fu.lookupPath(path.dirname(lumbarFile));
43
4429 return exports.create(config);
45};
46
471exports.create = function(config) {
48168 var packageList, moduleList;
49
50168 function loadPackageList() {
51168 if (!config.packages) {
52128 config.packages = { web: { name: '' } };
53 }
54
55168 packageList = _.keys(config.packages);
56 }
57168 function loadModuleList() {
58168 if (!config.modules) {
591 throw new Error('No modules object defined: ' + JSON.stringify(config, undefined, 2));
60 }
61167 moduleList = _.keys(config.modules);
62 }
63
64168 loadPackageList();
65168 loadModuleList();
66
67167 return {
68 /** @typedef {Object} The raw lumbar file as a JSON object. */
69 attributes: config,
70 loadPrefix: function() {
7151 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() {
8230 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) {
941868 if (config && config.packages && config.packages[pkg]) {
951593 return config.packages[pkg].combine;
96 }
97275 return false;
98 },
99 platformList: function(pkg) {
100103 if (!pkg) {
10159 return config.platforms || [''];
102 } else {
10344 if (config.packages[pkg]) {
10444 return config.packages[pkg].platforms || this.platformList();
105 }
1060 return this.platformList();
107 }
108 },
109
110 moduleList: function(pkg) {
111246 return (config.packages[pkg] || {}).modules || _.keys(config.modules);
112 },
113
114 module: function(name) {
115493 var ret = config.modules[name];
116493 if (ret) {
117490 ret.name = name;
118 }
119493 return ret;
120 },
121 isAppModule: function(module) {
12274 var app = config.application;
12374 return (app && app.module) === (module.name || module);
124 },
125 scopedAppModuleName: function(module) {
12644 var app = config.application;
12744 if (this.isAppModule(module)) {
1284 return 'module.exports';
129 } else {
13040 var app = config.application;
13140 return app && app.name;
132 }
133 },
134
135 routeList: function(module) {
13622 return config.modules[module].routes;
137 },
138
139 serialize: function() {
1402 function objectClone(object) {
14119 var clone = object;
142
14319 if (object && object.serialize) {
144 // Allow specialized objects to handle themselves
1450 clone = object.serialize();
14619 } else if (_.isArray(object)) {
1471 clone = _.map(object, objectClone);
14818 } else if (_.isObject(object)) {
14912 clone = {};
15012 _.each(object, function(value, name) {
15115 clone[name] = objectClone(value);
152 });
153 }
154
155 // Collapse simple resources
15619 if (clone.src && _.keys(clone).length === 1) {
1570 clone = clone.src;
158 }
159
16019 return clone;
161 }
162
1632 return objectClone(this.attributes);
164 }
165 };
166};
167
168

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

94%
110
104
6
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 fs = require('fs'),
4 fu = require('./fileUtil');
5
61function Context(options, config, plugins, libraries, event) {
72184 this._package = options.package;
82184 this._platform = options.platform;
92184 this._plugins = plugins;
102184 this.mode = options.mode;
112184 this.module = options.module;
122184 this.fileConfig = options.fileConfig;
132184 this.resource = options.resource;
142184 this.config = config;
152184 this.libraries = libraries || options.libraries;
162184 this.event = event || options.event;
17}
181Context.prototype = {
19 fileUtil: fu,
20
21 clone: function(options) {
222020 var ret = new Context(this, this.config);
232020 ret.parent = this;
242020 var prototype = Object.keys(Context.prototype);
252020 for (var name in this) {
2662469 if (this.hasOwnProperty(name) && prototype.indexOf(name) === -1) {
2736209 ret[name] = this[name];
28 }
29 }
302020 if (options) {
31289 _.extend(ret, options);
32289 ret._package = options.package || this._package;
33289 ret._platform = options.platform || this._platform;
34 }
352020 return ret;
36 },
37
38 fileNamesForModule: function(mode, moduleName, callback) {
3983 var context = this.clone();
4083 context.mode = mode;
4183 context.module = moduleName && this.config.module(moduleName);
4283 if (moduleName && !context.module) {
432 return callback(new Error('Unknown module "' + moduleName + '"'));
44 }
45
4681 this.plugins.outputConfigs(context, function(err, configs) {
4781 if (err) {
480 return callback(err);
49 }
50
5181 async.map(configs, function(config, callback) {
52107 var fileContext = context.clone();
53107 fileContext.fileConfig = config;
54107 fileContext._plugins.fileName(fileContext, function(err, fileName) {
55107 config.fileName = fileName;
56107 callback(err, config);
57 });
58 },
59 callback);
60 });
61 },
62
63 loadResource: function(resource, callback) {
64417 if (!callback) {
65 // if only single param, assume as callback and resource from context
660 resource = this.resource;
670 callback = resource;
68 }
69
70417 var fileInfo = {name: resource.hasOwnProperty('sourceFile') ? resource.sourceFile : resource.src};
71
72417 function loaded(err, data) {
73 /*jshint eqnull: true */
74417 if (err) {
754 var json = '';
764 try {
77 // Output JSON for the resource... but protect ourselves from a failure masking a failure
784 resource = _.clone(resource);
794 delete resource.library;
804 delete resource.enoent;
814 json = '\n\t' + JSON.stringify(resource);
82 } catch (err) { /* NOP */ }
83
844 var errorWrapper = new Error('Failed to load resource "' + fileInfo.name + '"' + json + '\n\t' + (err.stack || err));
854 errorWrapper.source = err;
864 errorWrapper.code = err.code;
874 callback(errorWrapper);
884 return;
89 }
90413 fileInfo.inputs = data.inputs;
91413 fileInfo.generated = data.generated;
92413 fileInfo.noSeparator = data.noSeparator;
93413 fileInfo.ignoreWarnings = data.ignoreWarnings || resource.ignoreWarnings;
94413 fileInfo.content = data.data != null ? data.data : data;
95
96 // Ensure that we dump off the stack
97413 _.defer(function() {
98413 callback(err, fileInfo);
99 });
100 }
101
102417 if (typeof resource === 'function') {
103198 resource(this, loaded);
104219 } else if (resource.src) {
105 // Assume a file page, attempt to load
106206 fu.readFile(resource.src, loaded);
107 } else {
10813 loaded(undefined, {data: '', noSeparator: true, inputs: resource.dir ? [resource.dir] : []});
109 }
110
111417 return fileInfo;
112 },
113
114 outputFile: function(writer, callback) {
115144 var context = this;
116144 context.plugins.file(context, function(err) {
117144 if (err) {
1180 return callback(err);
119 }
120
121144 context.plugins.fileName(context, function(err, fileName) {
122144 if (err) {
1230 return callback(err);
124 }
125
126144 context.buildPath = (fileName.root ? '' : context.platformPath) + fileName.path + '.' + fileName.extension;
127144 context.fileName = context.outdir + '/' + context.buildPath;
128144 writer(function(err, data) {
129144 data = _.defaults({
130 fileConfig: context.fileConfig,
131 platform: context.platform,
132 package: context.package,
133 mode: context.mode
134 }, data);
135
136144 if (err) {
1373 fs.unlink(context.fileName, function() { /* NOP To Prevent warning */});
1383 data.error = err;
139 }
140144 context.event.emit('output', data);
141
142144 context.fileCache = undefined;
143144 callback(err, data);
144 });
145 });
146 });
147 },
148
149 get description() {
150542 var ret = 'package:' + this.package + '_platform:' + this.platform;
151542 if (this.mode) {
152342 ret += '_mode:' + this.mode;
153 }
154542 if (this.fileName) {
155115 ret += '_config:' + this.fileName;
156 }
157542 if (this.module) {
158331 ret += '_module:' + (this.module.name || this.module);
159 }
160542 if (this.resource) {
161 // TODO : Anything better for this?
16219 ret += '_resource:' + (this.resource.src || this.resource);
163 }
164542 return ret;
165 },
166
1671933 get plugins() { return this._plugins; },
168
1695691 get package() { return this._package; },
1704170 get platform() { return this._platform; },
171 get platformPath() {
172156 return this.platform ? this.platform + '/' : '';
173 },
174
175 get combined() {
1761868 return this.config.combineModules(this.package);
177 },
178 get baseName() {
179225 if (!this.combined) {
180157 return this.module.name;
181 } else {
18268 return (this.config.attributes.packages[this.package] || {}).name || this.package;
183 }
184 },
185
186 get resources() {
187289 if (this.parent) {
1880 return this.parent.resources;
189 } else {
190289 return this._resources;
191 }
192 },
193 set resources(value) {
194363 if (this.parent) {
195327 delete this.parent;
196 }
197363 this._resources = value;
198 }
199};
200
2011module.exports = Context;
202

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

92%
176
163
13
LineHitsSource
11var _ = require('underscore'),
2 EventEmitter = require('events').EventEmitter,
3 fs = require('fs'),
4 path = require('path'),
5 handlebars = require('handlebars');
6
71const EMFILE_RETRY = 250;
8
91var fileCache = {};
10
111exports = module.exports = new EventEmitter();
12
131function cacheRead(path, exec, callback) {
14350 path = exports.resolvePath(path);
15
16350 var cache = fileCache[path];
17350 if (cache) {
18192 if (cache.data) {
19144 callback(undefined, cache);
20 } else {
2148 cache.pending.push(callback);
22 }
23192 return;
24 }
25
26158 cache = fileCache[path] = {
27 pending: [callback],
28 artifacts: {}
29 };
30
31158 exec(path, function _callback(err, data) {
32158 if (err && err.code === 'EMFILE') {
330 setTimeout(exec.bind(this, path, _callback), EMFILE_RETRY);
34 } else {
35158 if (err) {
362 delete fileCache[path];
37 }
38
39158 cache.data = data;
40158 cache.pending.forEach(function(callback) {
41206 callback(err, cache);
42 });
43158 exports.emit('cache:set', path);
44 }
45 });
46}
47
481exports.resetCache = function(filePath) {
49253 filePath = filePath && path.normalize(filePath);
50253 exports.emit('cache:reset', filePath);
51
52253 if (filePath) {
53177 filePath = exports.resolvePath(filePath);
54177 delete fileCache[filePath];
55 } else {
5676 fileCache = {};
57 }
58};
59
601var lookupPath;
611exports.resolvePath = function(pathName) {
62 // Poormans path.resolve. We aren't able to use the bundled path.resolve due to
63 // it throwing sync EMFILE errors without a type to key on.
641420 if (lookupPath
65 && (pathName[0] !== '/' && pathName.indexOf(':/') === -1 && pathName.indexOf(':\\') === -1)
66 && pathName.indexOf(lookupPath) !== 0) {
67958 return lookupPath + pathName;
68 } else {
69462 return pathName;
70 }
71};
721exports.makeRelative = function(pathName) {
73581 if (pathName.indexOf(lookupPath) === 0) {
74549 return pathName.substring(lookupPath.length);
75 } else {
7632 return pathName;
77 }
78};
79
801exports.lookupPath = function(pathName) {
81152 if (pathName !== undefined) {
8292 lookupPath = pathName;
8392 if (lookupPath && !/\/$/.test(lookupPath)) {
8438 lookupPath += '/';
85 }
86 }
87152 return lookupPath;
88};
89
901exports.stat = function(file, callback) {
91808 fs.stat(file, function(err, stat) {
92808 if (err && err.code === 'EMFILE') {
930 setTimeout(exports.stat.bind(exports, file, callback), EMFILE_RETRY);
94 } else {
95808 callback(err, stat);
96 }
97 });
98};
99
1001exports.readFileSync = function(file) {
10137 return fs.readFileSync(exports.resolvePath(file));
102};
1031exports.readFile = function(file, callback) {
104230 cacheRead(file, fs.readFile.bind(fs), function(err, cache) {
105230 callback(err, cache && cache.data);
106 });
107};
1081exports.readFileArtifact = function(file, name, callback) {
10959 cacheRead(file, fs.readFile.bind(fs), function(err, cache) {
11059 var artifacts = cache.artifacts;
11159 callback(err, {data: cache.data, artifact: artifacts[name]});
112 });
113};
1141exports.setFileArtifact = function(path, name, artifact) {
11525 path = exports.resolvePath(path);
116
11725 var cache = fileCache[path];
11825 if (cache) {
11925 cache.artifacts[name] = artifact;
120 }
121};
122
1231exports.readdir = function(dir, callback) {
12461 cacheRead(dir, fs.readdir.bind(fs), function(err, cache) {
12561 callback(err, cache && cache.data);
126 });
127};
128
1291exports.ensureDirs = function(pathname, callback) {
130243 var dirname = path.dirname(pathname);
131243 exports.stat(dirname, function(err) {
132243 if (err && err.code === 'ENOENT') {
133 // If we don't exist, check to see if our parent exists before trying to create ourselves
13442 exports.ensureDirs(dirname, function() {
13542 fs.mkdir(dirname, parseInt('0755', 8), function _callback(err) {
13642 if (err && err.code === 'EMFILE') {
1370 setTimeout(fs.mkdir.bind(fs, dirname, parseInt('0755', 8), _callback), EMFILE_RETRY);
138 } else {
139 // Off to the races... and we lost.
14042 callback(err && err.code === 'EEXIST' ? undefined : err);
141 }
142 });
143 });
144 } else {
145201 callback();
146 }
147 });
148};
149
1501exports.writeFile = function(file, data, callback) {
151136 exports.resetCache(file);
152
153136 exports.ensureDirs(file, function(err) {
154136 if (err) {
1550 return callback(err);
156 }
157
158136 fs.writeFile(file, data, 'utf8', function _callback(err) {
159136 if (err && err.code === 'EMFILE') {
1600 setTimeout(fs.writeFile.bind(fs, file, data, 'utf8', _callback), EMFILE_RETRY);
161 } else {
162136 callback(err);
163 }
164 });
165 });
166};
167
168/**
169 * Takes a given input and returns the files that are represented.
170 *
171 * pathname may be:
172 * a resource object
173 * a path on the file system
174 * an array of resources
175 */
1761exports.fileList = function(pathname, extension, callback, dirList, resource, srcDir) {
177866 if (_.isFunction(extension)) {
1785 callback = extension;
1795 extension = /.*/;
180 }
181
182866 if (_.isArray(pathname)) {
183303 var files = pathname;
184303 pathname = '';
185303 if (!files.length) {
186123 return callback(undefined, []);
187 }
188180 return handleFiles(false, undefined, _.uniq(files));
189563 } else if (!dirList) {
190402 if (pathname.src) {
1910 resource = resource || pathname;
1920 pathname = pathname.src;
193 }
194
195402 pathname = exports.resolvePath(pathname);
196 }
197563 if (resource && resource.src) {
198183 resource = _.clone(resource);
199183 delete resource.src;
200 }
201
202563 function handleFiles(dirname, err, files, srcDir) {
203238 if (err) {
2040 return callback(err);
205 }
206
207238 var ret = [],
208 count = 0,
209 expected = files.length,
210 prefix = pathname ? pathname.replace(/\/$/, '') + '/' : '';
211
212238 function complete(files, index) {
213599 count++;
214
215599 ret[index] = files;
216
217599 if (count === expected) {
218237 ret = _.flatten(ret);
219
220237 if (srcDir) {
22157 ret = ret.map(function(file) {
222124 if (!file.src && !file.dir) {
2230 file = { src: file };
224 }
225124 file.srcDir = srcDir;
226124 return file;
227 });
228 }
229
230237 if (dirname) {
23157 ret.push(_.defaults({dir: dirname}, resource));
23257 ret = ret.sort(function(a, b) {
233241 return (a.dir || a.src || a).localeCompare(b.dir || b.src || b);
234 });
235 }
236
237237 callback(undefined, ret);
238 }
239 }
240
241238 if (!files.length) {
2421 callback(undefined, []);
243 }
244
245238 files.forEach(function(file, index) {
246599 var fileResource = resource;
247599 if (file.src) {
248183 fileResource = resource || file;
249183 file = file.src;
250416 } else if (_.isObject(file)) {
25164 complete(file, index);
25264 return;
253 }
254
255535 exports.fileList(prefix + file, extension, function(err, files) {
256535 if (err) {
2570 callback(err);
2580 return;
259 }
260
261535 complete(files, index);
262 }, dirname, fileResource, srcDir);
263 });
264 }
265
266563 exports.stat(pathname, function(err, stat) {
267563 if (err) {
26853 if (err.code === 'ENOENT') {
26953 callback(undefined, [ _.extend({src: exports.makeRelative(pathname), enoent: true}, resource) ]);
270 } else {
2710 callback(err);
272 }
27353 return;
274 }
275
276510 if (stat.isDirectory()) {
27758 exports.readdir(pathname, function(err, files) {
27858 var _pathname = exports.makeRelative(pathname);
27958 handleFiles(_pathname, undefined, files, srcDir || _pathname);
280 });
281 } else {
282452 pathname = exports.makeRelative(pathname);
283
284452 var basename = path.basename(pathname),
285 namePasses = basename[0] !== '.' && basename !== 'vendor' && (!dirList || extension.test(pathname)),
286 ret = [];
287452 if (namePasses) {
288394 if (resource) {
289170 ret = [ _.defaults({src: pathname, srcDir: srcDir}, resource) ];
290224 } else if (srcDir) {
29171 ret = [ { src: pathname, srcDir: srcDir } ];
292 } else {
293153 ret = [ pathname ];
294 }
295 }
296452 callback(undefined, ret);
297 }
298 });
299};
300
301//accepts a template string or a filename ending in .handlebars
3021exports.loadTemplate = function(template, splitOnDelimiter, callback) {
30341 function compile(templateStr, callback) {
30430 try {
30530 if (splitOnDelimiter) {
30619 callback(null, templateStr.split(splitOnDelimiter).map(function(bit) {
30738 return handlebars.compile(bit);
308 }));
309 } else {
31011 callback(null, handlebars.compile(templateStr));
311 }
312 } catch (e) {
3130 callback(e);
314 }
315 }
31641 if (template.match(/\.handlebars$/)) {
31719 exports.readFileArtifact(template, 'template', function(err, data) {
31819 if (err) {
3191 return callback(err);
320 }
321
32218 if (data.artifact) {
32310 callback(undefined, data.artifact);
324 } else {
3258 compile(data.data.toString(), function(err, data) {
3268 if (!err) {
3278 exports.setFileArtifact(template, 'template', data);
328 }
3298 callback(err, data);
330 });
331 }
332 });
333 } else {
33422 compile(template, callback);
335 }
336};
337

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

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

94%
186
176
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
91function Libraries(options) {
10139 this.options = options;
11139 this.mixins = [];
12139 this.configs = [];
13}
14
151Libraries.prototype.initialize = function(context, callback) {
16136 this.mixins = [];
17136 this.originalConfig = _.clone(context.config.attributes);
18
19136 function normalize(libraries) {
20272 if (_.isString(libraries)) {
212 return [libraries];
22 } else {
23270 return _.map(libraries, function (name) {
2469 if (_.isString(name)) {
2511 return path.normalize(name);
26 } else {
2758 return name;
28 }
29 });
30 }
31 }
32
33136 var commandLineLibraries = normalize(this.options.libraries || []),
34 configLibraries = normalize(context.config.attributes.libraries || context.config.attributes.mixins || []),
35 bowerLibraries = this.bowerLibraries(context) || [],
36
37 allLibraries = _.union(commandLineLibraries, configLibraries, bowerLibraries);
38
39136 delete context.config.attributes.mixins;
40
41136 async.forEachSeries(allLibraries, _.bind(this.load, this, context), callback);
42};
43
441Libraries.prototype.bowerLibraries = function(context) {
45137 try {
46137 fs.statSync(fu.resolvePath('bower.json'));
47
482 var bowerDir = bower.config.directory,
49 possibleModules = fs.readdirSync(bowerDir);
50
511 return possibleModules
52 .map(function(name) {
533 return path.normalize(path.join(bowerDir, name));
54 })
55 .filter(function(name) {
563 try {
573 fs.statSync(path.join(name, 'lumbar.json'));
582 return true;
59 } catch (err) {
60 /* NOP */
61 }
62 });
63 } catch (err) {
64136 context.event.emit('debug', err);
65 }
66};
67
681Libraries.prototype.load = function(context, libraryConfig, callback) {
69 // Allow mixins to be passed directly
7074 var root = libraryConfig.root,
71 configPath,
72 self = this;
73
74 // Or as a file reference
7574 if (!_.isObject(libraryConfig)) {
768 root = root || libraryConfig;
77
78 // If we have a dir then pull lumbar.json from that
798 try {
808 var stat = fs.statSync(fu.resolvePath(libraryConfig));
818 if (stat.isDirectory()) {
823 libraryConfig = libraryConfig + '/lumbar.json';
835 } else if (root === libraryConfig) {
84 // If we are a file the root should be the file's directory unless explicitly passed
855 root = path.dirname(root);
86 }
87 } catch (err) {
880 return callback(err);
89 }
90
918 configPath = fu.resolvePath(libraryConfig);
928 libraryConfig = config.readConfig(configPath);
93 }
94
95 // To make things easy force root to be a dir
9674 if (root && !/\/$/.test(root)) {
9729 root = root + '/';
98 }
99
10074 if (!libraryConfig.name) {
1014 return callback(new Error('Mixin with root "' + root + '" is missing a name.'));
102 }
103
10470 var mixins = libraryConfig.mixins,
105 toRegister = {};
10670 delete libraryConfig.mixins;
107
10870 function mapMixin(mixin, name) {
109 // Only register once, giving priority to an explicitly defined mixin
11047 if (!toRegister[name]) {
11146 toRegister[name] = {
112 serialize: function() {
1130 return {name: this.name, library: this.parent.name};
114 },
115 attributes: mixin,
116 parent: libraryConfig,
117 root: root
118 };
119 }
120 }
121
122 // Read each of the mixins that are defined in the config
12370 _.each(mixins, mapMixin, this);
124
125 // Make mixin modules accessible as normal mixins as well
12670 _.each(libraryConfig.modules, mapMixin, this);
127
128 // After we've pulled everything in register
12970 _.each(toRegister, function(mixin, name) {
13046 this.mixins[name] = this.mixins[name] || [];
13146 var list = this.mixins[name];
13246 list.push(mixin);
133 }, this);
134
135 // Run all of the plugins that are concerned with this.
13670 libraryConfig.root = root;
13770 libraryConfig.path = configPath;
13870 context.loadedLibrary = libraryConfig;
13970 context.plugins.loadMixin(context, function(err) {
14070 delete libraryConfig.root;
141
142 // And then splat everything else into our config
14370 _.defaults(context.config.attributes, _.omit(context.loadedLibrary, 'name', 'path'));
144
14570 libraryConfig.serialize = function() {
1460 return { library: this.name };
147 };
148
14970 libraryConfig.root = root;
15070 self.configs.push(libraryConfig);
151
15270 callback(err);
153 });
154};
155
1561Libraries.prototype.findDecl = function(mixins, mixinName) {
1579 if (!mixinName.name) {
1586 mixinName = {name: mixinName};
159 }
160
1619 return _.find(mixins, function(mixinDecl) {
16211 return (mixinDecl.name || mixinDecl) === mixinName.name
163 && (!mixinDecl.library || mixinDecl.library === mixinName.library);
164 });
165};
166
1671Libraries.prototype.moduleMixins = function(module) {
168 // Perform any nested mixin lookup
169296 var mixins = _.clone(module.mixins || []);
170296 for (var i = 0, len = mixins.length; i < len; i++) {
17164 var firstInclude = mixins[i],
172 mixin = this.getMixin(firstInclude),
173 added = [i, 0];
174
17562 if (!mixin) {
1760 throw new Error('Unable to find mixin "' + ((firstInclude && firstInclude.name) || firstInclude) + '"');
177 }
178
179 // Check if we need to include any modules that this defined
18062 _.each(mixin.attributes.mixins, function(mixinInclude) {
1814 if (!this.findDecl(mixins, mixinInclude)) {
1824 added.push(mixinInclude);
183 }
184 }, this);
185
186 // If we've found any new mixins insert them at the current spot and iterate
187 // over those items
18862 if (added.length > 2) {
1894 mixins.splice.apply(mixins, added);
1904 i--;
191 }
192 }
193
194 // Extend the module with each of the mixins content, giving priority to the module
195294 return _.map(mixins.reverse(), function(mixin) {
19662 var mixinConfig = mixin.name && mixin,
197 name = mixin;
19862 if (mixinConfig) {
19920 mixinConfig = _.clone(mixinConfig);
20020 delete mixinConfig.library;
20120 delete mixinConfig.container;
202 }
20362 mixin = _.extend(
204 {},
205 this.getMixin(name),
206 mixinConfig);
20762 if (!mixin.attributes) {
2080 throw new Error('Mixin "' + (name.name || name) + '" is not defined.');
209 }
210
211 // Save a distinct instance of the config for resource extension
21262 if (mixinConfig) {
21320 mixinConfig = _.clone(mixinConfig);
21420 delete mixinConfig.overrides;
21520 delete mixinConfig.name;
216 }
217
21862 return {
219 library: mixin,
220 mixinConfig: mixinConfig
221 };
222 }, this);
223};
224
2251Libraries.prototype.mapFiles = function(value, library, config) {
226165 var files = _.map(value, function(resource) {
227248 return this.mapFile(resource, library, config);
228 }, this);
229411 files = _.filter(files, function(file) { return file; });
230
231164 return files;
232};
2331Libraries.prototype.mapFile = function(resource, library, config) {
234 // If explicitly declared the resource library takes precedence
235248 if (_.isString(resource.library || resource.mixin)) {
2363 library = this.getConfig(resource.library || resource.mixin);
2373 if (!library) {
2381 throw new Error('Mixin "' + (resource.library || resource.mixin) + '" not found');
239 }
2402 delete resource.mixin;
241 }
242
243247 var bowerPath;
244247 if (_.isString(resource.bower)) {
2452 bowerPath = path.join(bower.config.directory, resource.bower);
246 }
247
248 // If no mixin was defined on either side then return the identity
249247 if (!library && !bowerPath) {
250177 return resource;
251 }
252
25370 if (_.isString(resource)) {
25457 resource = {src: resource};
255 } else {
25613 resource = _.clone(resource);
257 }
258
25970 var src = resource.src || resource.dir;
260
261 // Include any config information such as env or platform that may have been
262 // specified on the library settings
26370 _.extend(resource, config);
264
26570 if (src) {
26668 var librarySrc = bowerPath || library.root || '';
26768 librarySrc = librarySrc ? path.join(librarySrc, src) : src;
268
26968 var override = library && library.overrides && library.overrides[src];
27068 if (override) {
2716 resource.originalSrc = librarySrc;
2726 librarySrc = _.isString(override) ? override : src;
27362 } else if (override === false) {
2741 return;
275 }
276
27767 if (resource.src) {
27866 resource.src = librarySrc;
2791 } else if (resource.dir) {
2801 resource.dir = librarySrc;
281 }
282 }
283
28469 resource.library = library;
28569 return resource;
286};
287
2881Libraries.prototype.getMixin = function(name) {
289126 var mixins = (this.mixins && this.mixins[name.name || name]) || [],
290 library = name.library || name.container;
291126 if (mixins.length > 1 && !library) {
2921 throw new Error(
293 'Duplicate mixins found for "' + (name.name || name) + '"'
294 + _.map(mixins, function(mixin) {
2952 return ' parent: "' + mixin.parent.name + '"';
296 }).join(''));
297 }
298
299125 if (library) {
3009 if (name.name === undefined) {
3010 var found = _.find(this.configs, function(config) {
3020 return config.name === library;
303 });
3040 if (!found) {
3050 throw new Error('Unable to find library "' + library + '"');
306 }
3070 return found;
308 }
309
3109 var found = _.find(mixins, function(mixin) {
31117 return mixin.parent.name === library;
312 });
3139 if (found) {
3148 return found;
315 } else {
3161 throw new Error('Mixin named "' + name.name + '" not found in library "' + library + '"');
317 }
318116 } else if (mixins.length === 1) {
319116 return mixins[0];
320 }
321};
3221Libraries.prototype.getConfig = function(name) {
32313 return _.find(this.configs, function(config) { return config.name === name; });
324};
325
3261Libraries.prototype.resolvePath = function(name, mixin) {
32746 if (!mixin) {
32837 return name;
329 }
330
3319 var override = mixin.overrides && mixin.overrides[name];
3329 if (override) {
3332 return _.isString(override) ? override : name;
334 }
335
3367 return mixin.root + name;
337};
338
3391Libraries.prototype.mergeHash = function(hashName, input, mixin, output) {
34064 if (mixin[hashName]) {
341 // Close the value to make sure that we are not overriding anything
34210 if (!output[hashName] || output[hashName] === input[hashName]) {
3438 output[hashName] = _.clone(input[hashName] || {});
344 }
34510 _.each(mixin[hashName], function(value, key) {
34616 if (!input[hashName] || !(key in input[hashName])) {
34712 output[hashName][key] = value;
348 }
349 });
35010 return true;
351 }
352};
3531Libraries.prototype.mergeFiles = function(fieldName, input, mixinData, output, library) {
35436 if (mixinData[fieldName]) {
3558 mixinData = _.isArray(mixinData[fieldName]) ? mixinData[fieldName] : [mixinData[fieldName]];
356
3578 var configData = input[fieldName] || [];
3588 if (!output[fieldName] || configData === output[fieldName]) {
3596 output[fieldName] = _.clone(configData);
360 }
3618 if (!_.isArray(configData)) {
3622 configData = [configData];
363 }
3648 if (!_.isArray(output[fieldName])) {
3651 output[fieldName] = [output[fieldName]];
366 }
367
368 // Insert point is at the start of the upstream list, which we are
369 // assuming occurs at length postions from the end.
3708 _.each(mixinData, function(value) {
371 //Make the include relative to the mixin
37211 value = (library.root || '') + value;
373
37411 output[fieldName].splice(
375 output[fieldName].length - configData.length,
376 0,
377 {src: value, library: library});
378 });
379
3808 return true;
381 }
382};
383
3841module.exports = Libraries;
385

/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
3025 options = _.clone(options || {});
3125 options.plugins = _.clone(options.plugins || []);
32
3325 function logError(err) {
3457 if (err) {
353 event.emit('error', err);
36 }
37 }
38
3925 var event = new EventEmitter(),
40 watch,
41 watchContext;
42
4325 function watchOutputHandler(status) {
44102 if (!watch) {
45 // We've been cleaned up but residuals may still exist, do nothing on this exec
4614 return;
47 }
48
4988 if (status.fileConfig.isPrimary) {
5035 delete status.fileConfig;
5153 } 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
5776 var originalContext = watchContext;
5876 watch.moduleOutput(status, function() {
5935 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
6435 stateMachine.buildPlatform(watchContext.clone(status), logError);
65 });
66 }
67
6825 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) {
9418 if (!fs.watch) {
950 throw new Error('Watch requires fs.watch, introduced in Node v0.6.0');
96 }
97
9818 ChildPool.isBackground(true);
99
10018 watch = new WatchManager();
10118 watch.on('watch-change', function(info) {
10243 event.emit('watch-change', info);
103 });
104
10518 var self = this;
10618 stateMachine.loadConfig(lumbarFile, event, options, function(err, context) {
10718 if (err) {
1080 logError(err);
109 }
110
11118 if (!callback) {
11218 callback = modules;
11318 modules = undefined;
114 }
115
11618 watchContext = context;
117
118 // Watch for changes in the config file
11923 var mixinPaths = _.filter(_.pluck(context.libraries.configs, 'path'), function(path) { return path; });
12018 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
12618 if (err) {
1270 return;
128 }
129
130 // Watch the individual components
13118 event.removeListener('output', watchOutputHandler);
13218 event.on('output', watchOutputHandler);
133
134 // Actual build everything
13518 var packages = packageName ? [packageName] : context.config.packageList();
13618 packages.forEach(function(name) {
13722 stateMachine.buildPackages(context, name, modules, logError);
138 });
139 });
140 },
141 unwatch: function() {
14214 event.removeListener('output', watchOutputHandler);
14314 if (watch) {
14414 watch.removeAllListeners();
14514 watch.reset();
14614 watch = undefined;
14714 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%
108
107
1
LineHitsSource
11var _ = require('underscore'),
2 path = require('path');
31const corePlugins = [
4 'mixin',
5 'styles-output', 'scripts-output', 'static-output',
6 'scope', 'router', 'template', 'inline-styles',
7 'coffee-script', 'stylus', 'handlebars',
8 'module-map', 'package-config', 'stylus-config',
9 'update-externals',
10 '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) {
1720 globalPlugins[name] = plugin;
1820 plugin.id = name;
19};
20
211exports.plugin('module-map', require('./plugins/module-map'));
221exports.plugin('package-config', require('./plugins/package-config'));
231exports.plugin('router', require('./plugins/router'));
241exports.plugin('scope', require('./plugins/scope'));
251exports.plugin('stylus', require('./plugins/stylus'));
261exports.plugin('stylus-config', require('./plugins/stylus-config'));
271exports.plugin('coffee-script', require('./plugins/coffee-script'));
281exports.plugin('handlebars', require('./plugins/handlebars'));
291exports.plugin('inline-styles', require('./plugins/inline-styles'));
301exports.plugin('inline-styles-resources', require('./plugins/inline-styles-resources'));
311exports.plugin('mixin', require('./plugins/mixin'));
321exports.plugin('update-externals', require('./plugins/update-externals'));
331exports.plugin('template', require('./plugins/template'));
341exports.plugin('styles', require('./plugins/styles.js'));
351exports.plugin('server-scripts', require('./plugins/server-scripts.js'));
361exports.plugin('scripts', require('./plugins/scripts.js'));
371exports.plugin('static', require('./plugins/static.js'));
381exports.plugin('styles-output', require('./plugins/styles-output.js'));
391exports.plugin('scripts-output', require('./plugins/scripts-output.js'));
401exports.plugin('static-output', require('./plugins/static-output.js'));
41
421exports.create = function(options) {
43129 var plugins;
44129 var modes; // all registered modes
45129 var pluginModes; // map of modes and plugins scoped to the mode
46129 var modeAll; // plugins that are scoped to all modes
47
48129 function runPlugins(context, methodName, complete, failOver, noMode) {
492982 var len = 0,
50 pluginMode = pluginModes[context.mode] || [];
51
522982 return (function next(complete) {
53 /*jshint boss:true */
548379 var plugin;
558379 while (plugin = plugins[len++]) {
56 // if plugin shouldn't work with current mode, go to next
5755235 if (!noMode
58 && (!context.mode || pluginMode.indexOf(plugin) < 0)
59 && modeAll.indexOf(plugin) < 0) {
6024458 continue;
61 }
62
6330777 var method = plugin[methodName];
6430777 if (method) {
656074 if (complete) {
665938 process.nextTick(function() {
675938 method.call(plugin, context, next, complete);
68 });
695938 return;
70 } else {
71136 return method.call(plugin, context, next, complete);
72 }
73 }
74 }
75
76 // We're done, send data back
772305 if (complete) {
78 // async
79 // Clear out our stack under async mode to try to keep the stack somewhat sane.
802118 process.nextTick(function() {
812118 complete(undefined, failOver && failOver());
82 });
83 } else {
84 // sync
85187 return failOver && failOver();
86 }
87 })(complete);
88 }
89
90129 function registerPlugin(plugin) {
912407 var _plugin = globalPlugins[plugin] || plugin;
92
932407 var mode = _plugin.mode;
942407 if (mode) {
952281 if (_.isString(mode)) {
961770 mode = [mode];
97 }
982281 _.each(mode, function(_mode) {
992792 if (mode === 'all') {
100 // allow plugins to contribute new modes and participate in all modes
1010 modeAll.push(_plugin);
102 } else {
1032792 if (modes.indexOf(_mode) < 0) {
104385 modes.push(_mode);
105385 pluginModes[_mode] = [];
106 }
1072792 pluginModes[_mode].push(_plugin);
108 }
109 });
110 } else {
111126 modeAll.push(_plugin);
112 }
1132407 plugins.push(_plugin);
1142407 plugins.sort(function(a, b) {
11531923 return (a.priority || 50) - (b.priority || 50);
116 });
117 }
118
119129 return {
120 get: function(name) {
121 // Find the plugin with this id, if one exists
12268 var plugin = plugins.reduce(function(plugin, left) {
1231224 return plugin.id === name ? plugin : left;
124 });
125
126 // If the plugin was not found do not return the last item in the reduce
12768 if (plugin.id === name) {
12866 return plugin;
129 }
130 },
131 use: function(plugin) {
13213 if (plugin.path || (_.isString(plugin) && !globalPlugins[plugin])) {
1331 var pluginPath = plugin.path || plugin;
1341 var options = plugin.options;
1351 try {
1361 plugin = require(pluginPath);
137 } catch (e) {
1381 plugin = require(path.resolve(process.cwd(), fileUtils.lookupPath()) + '/node_modules/' + pluginPath);
139 }
1401 if ('function' === typeof plugin) {
1411 plugin = plugin(options);
142 }
143 }
14413 registerPlugin(plugin);
145 },
146
147 initialize: function(config) {
148 // reset
149129 plugins = [];
150129 modes = []; // all registered modes
151129 pluginModes = {}; // map of modes and plugins scoped to the mode
152129 modeAll = []; // plugins that are scoped to all modes
153
154 // load the core plugins
155129 if (!options.ignoreCorePlugins) {
156126 corePlugins.forEach(registerPlugin);
157 }
158
159129 var self = this;
160129 function plugin(plugins) {
161258 if (plugins) {
16234 plugins.forEach(self.use, self);
163 }
164 }
165
166 // load command line plugins
167129 plugin(options.plugins);
168
169 // load lumbar.json plugins
170129 plugin(config.attributes.plugins);
171 },
172
173 loadMixin: function(context, complete) {
17470 runPlugins(context, 'loadMixin', complete, undefined, true);
175 },
176 loadConfig: function(context, complete) {
177128 runPlugins(context, 'loadConfig', complete, undefined, true);
178 },
179 outputConfigs: function(context, complete) {
180238 runPlugins(context, 'outputConfigs', complete, function() {
181 // Default to a one to one mapping for a given {platform, package, module, mode} combo
182236 return [ {} ];
183 });
184 },
185 modeComplete: function(context, complete) {
186184 runPlugins(context, 'modeComplete', complete);
187 },
188 fileName: function(context, complete) {
189251 runPlugins(context, 'fileName', complete);
190 },
191
192 fileFilter: function(context) {
193323 return runPlugins(context, 'fileFilter');
194 },
195 moduleResources: function(context, complete) {
196435 runPlugins(context, 'moduleResources', complete, function() {
197240 var module = context.module;
198240 return (module[context.mode] || []).slice();
199 });
200 },
201 resourceList: function(context, complete) {
202992 runPlugins(context, 'resourceList', complete, function() { return [context.resource]; });
203 },
204
205 file: function(context, complete) {
206144 runPlugins(context, 'file', complete);
207 },
208 module: function(context, complete) {
209274 runPlugins(context, 'module', complete);
210 },
211 resource: function(context, complete) {
212788 runPlugins(context, 'resource', complete, function() { return context.resource; });
213 },
214 modes: function() {
21547 return modes;
216 }
217 };
218};
219

/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) {
10264 var resource = context.resource;
11264 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 {
36262 next(complete);
37 }
38 }
39};
40

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

91%
94
86
8
LineHitsSource
1/**
2 * Template Plugin : Includes handlebars templates associated with a given file
3 * when said file is imported.
4 *
5 * Config:
6 * root:
7 * templates:
8 * template: Defines the template that is used to output the template in the module. See consts below.
9 * precompile:
10 * Flag/hash that enable precompilation. Truthy will enable precompilation. A hash with
11 * the key name "template" will override the rendering template. (See the template value above.)
12 * cache: Name of the javascript object that templates will be assigned to.
13 * Defaults to `$AppModule.templates` if an app module exists, otherwise `templates`
14 *
15 * Mixins:
16 * The template plugin will mixin any special values directly, giving priority to the local version.
17 *
18 */
191var _ = require('underscore'),
20 handlebars = require('handlebars'),
21 templateUtil = require('../templateUtil');
22
231handlebars.registerHelper('without-extension', function(str) {
241 return str.replace(/\.[a-zA-Z0-9]+$/, '');
25});
26
27
281const DEFAULT_TEMPLATE_TEMPLATE = "/* handsfree : {{{name}}}*/\n{{{templateCache}}}['{{{name}}}'] = {{handlebarsCall}}({{{data}}});\n";
29
301function ensureTemplateTemplates(context, complete) {
3138 if (!context.configCache.templateTemplate) {
3212 var templateTemplate = (context.config.attributes.templates && context.config.attributes.templates.template) || DEFAULT_TEMPLATE_TEMPLATE;
3312 context.fileUtil.loadTemplate(templateTemplate, false, function(err, compiled) {
3412 if (err) {
351 complete(err);
36 } else {
3711 context.configCache.templateTemplate = compiled;
3811 complete();
39 }
40 });
41 } else {
4226 complete();
43 }
44}
45
461function loadTemplate(name, resource, context, callback) {
4738 ensureTemplateTemplates(context, function(err) {
4838 if (err) {
491 return callback(err);
50 }
5137 var artifactType = 'template' + context.fileConfig.server;
5237 context.fileUtil.readFileArtifact(name, artifactType, function(err, cache) {
5337 if (err) {
540 callback(new Error('Failed to load template "' + name + '"\n\t' + err));
550 return;
56 }
57
5837 var artifact = cache.artifact || {},
59 data = artifact.data || cache.data.toString(),
60 attr = context.config.attributes,
61 templates = attr.templates || attr.views || {},
62 appModule = context.config.scopedAppModuleName(context.module),
63 templateCache = (context.config.attributes.templates && context.config.attributes.templates.cache)
64 || context.config.attributes.templateCache
65 || ((appModule ? appModule + '.' : '') + 'templates'),
66 template = context.configCache.templateTemplate;
67
68 // We have the template data, now convert it into the proper format
6937 name = templateUtil.escapeJsString(name);
7037 if (!cache.artifact) {
7116 if (templates.precompile) {
722 var options = context.fileCache.precompileTemplates;
732 if (!options) {
742 context.fileCache.precompileTemplates = options = _.clone(templates.precompile);
752 if (templates.knownHelpers || options.knownHelpers) {
760 options.knownHelpers = (options.knownHelpers || templates.knownHelpers).reduce(
77 function(value, helper) {
780 value[helper] = true;
790 return value;
80 }, {});
81 }
822 if (context.fileConfig.server && templates.server) {
831 _.extend(options, templates.server);
84 }
85 }
862 try {
872 data = handlebars.precompile(data, options);
88 } catch (err) {
890 return callback(err);
90 }
91 } else {
9214 data = "'" + templateUtil.escapeJsString(data) + "'";
93 }
9416 context.fileUtil.setFileArtifact(name, artifactType, {data: data, template: template});
95 }
96
97
9837 var mixinRoot = (resource.library && resource.library.root) || '';
9937 if (name.indexOf(mixinRoot) === 0) {
10037 name = name.substring(mixinRoot.length);
101 }
10237 if (templates.root && name.indexOf(templates.root) === 0) {
1033 name = name.substring(templates.root.length);
104 }
105
10637 callback(
107 undefined,
108 template({
109 name: name,
110 handlebarsCall: templates.precompile ? 'Handlebars.template' : 'Handlebars.compile',
111 templateCache: templateCache,
112 data: data
113 })
114 );
115 });
116 });
117}
118
1191module.exports = {
120 mode: 'scripts',
121 priority: 50,
122
123 loadMixin: function(context, next, complete) {
12470 var mixinTemplates = context.loadedLibrary.templates;
12570 if (mixinTemplates) {
1269 var templates = context.libraries.originalConfig.templates || {},
127 configTemplates = _.clone(context.config.attributes.templates || templates),
128 assigned = false;
129
1309 ['template', 'precompile', 'cache', 'root'].forEach(function(key) {
13136 if (_.has(mixinTemplates, key) && !_.has(templates, key)) {
1326 configTemplates[key] = mixinTemplates[key];
1336 assigned = true;
134 }
135 });
136
1379 if (_.has(mixinTemplates, 'knownHelpers')) {
1381 configTemplates.knownHelpers = (configTemplates.knownHelpers || []).concat(mixinTemplates.knownHelpers);
1391 assigned = true;
140 }
141
1429 if (assigned) {
1433 context.config.attributes.templates = configTemplates;
144 }
145 }
14670 next(complete);
147 },
148
149 resource: function(context, next, complete) {
150220 var resource = context.resource;
151
152220 if (/\.handlebars$/.test(resource.src) || resource.template) {
15326 var loadedTemplates = context.fileCache.loadedTemplates;
15426 if (!loadedTemplates) {
15525 loadedTemplates = context.fileCache.loadedTemplates = {};
156 }
157
15826 var generator = function(buildContext, callback) {
15926 var output = [],
160 inputs = [];
16126 context.fileUtil.fileList(resource.src, /\.handlebars$/, function(err, files) {
16226 if (err) {
1630 callback(err);
1640 return;
165 }
166
16726 function ignore(file) {
168117 return file.dir || loadedTemplates[file.src || file];
169 }
17026 function checkComplete() {
17163 if (inputs.length === files.length) {
172 // Sorting is effectively sorting on the file name due to the name comment in the template
17325 callback(undefined, {
174 inputs: inputs,
175 data: output.sort().join(''),
176 name: resource.src,
177 generated: true,
178 noSeparator: true,
179 ignoreWarnings: true
180 });
18125 return true;
182 }
183 }
184
18547 inputs = _.map(files.filter(ignore), function(input) { return input.src || input; });
18626 if (checkComplete()) {
1871 return;
188 }
189
19025 files.forEach(function(file) {
19158 if (ignore(file)) {
19220 return;
193 }
194
19538 var src = file.src || file;
19638 loadedTemplates[src] = true;
19738 loadTemplate(src, resource, context, function(err, data) {
19838 if (err) {
1991 return callback(err);
200 }
201
20237 output.push(data.data || data);
20337 inputs.push(src);
20437 checkComplete();
205 });
206 });
207 });
208 };
20926 generator.sourceFile = resource.src;
21026 complete(undefined, generator);
211 } else {
212194 next(complete);
213 }
214 }
215};
216

/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) {
8351 if (inlineStyles.isInline(context) && context.mode === 'styles') {
9 // Prevent stylesheet output if in inline mode
103 complete(undefined, []);
11348 } else if (inlineStyles.isInline(context)) {
126 next(function(err, scripts) {
136 complete(undefined, scripts.concat(context.module.styles || []));
14 });
15 } else {
16342 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) {
161502 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) {
2570 var mixinStyles = context.loadedLibrary.styles;
2670 if (mixinStyles) {
2718 var styles = context.libraries.originalConfig.styles || {},
28 configStyles = _.clone(context.config.attributes.styles || styles),
29 assigned = false;
30
3118 ['inline', 'inlineLoader'].forEach(function(key) {
3236 if ((key in mixinStyles) && !(key in styles)) {
336 configStyles[key] = mixinStyles[key];
34
356 assigned = true;
36 }
37 });
38
3918 if (assigned) {
405 context.config.attributes.styles = configStyles;
41 }
42 }
4370 next(complete);
44 },
45
46 outputConfigs: function(context, next, complete) {
47190 if (isInline(context) && context.mode === 'styles') {
48 // Prevent stylesheet output if in inline mode
492 complete(undefined, []);
50 } else {
51188 next(complete);
52 }
53 },
54
55 module: function(context, next, complete) {
56193 next(function(err) {
57193 if (err) {
580 return complete(err);
59 }
60
61193 if (isInline(context)) {
623 context.moduleResources = context.moduleResources.map(function(resource) {
639 if (resource.style || /\.css$/.test(resource.src)) {
643 var generator = function(context, callback) {
653 context.loadResource(resource, function(err, data) {
663 if (err) {
670 return callback(err);
68 }
69
703 var config = context.config,
71 loaderName = config.attributes.styles.inlineLoader || (config.scopedAppModuleName(context.module) + '.loader.loadInlineCSS');
723 callback(err, {
73 data: loaderName + '("'
74 + data.content
75 .replace(/\\/g, '\\')
76 .replace(/\n/g, '\\n')
77 .replace(/"/g, '\\"')
78 + '");\n',
79 inputs: data.inputs,
80 generated: true,
81 noSeparator: true
82 });
83 });
84 };
853 generator.style = true;
863 generator.sourceFile = resource.sourceFile || resource.src;
873 return generator;
88 } else {
896 return resource;
90 }
91 });
92 }
93
94193 complete();
95 });
96 }
97};
98

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

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

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

96%
60
58
2
LineHitsSource
11var _ = require('underscore');
2
31module.exports = {
4 priority: 1,
5
6 loadConfig: function(context, next, complete) {
7125 var modules = context.config.attributes.modules,
8 errored;
9125 _.each(context.libraries.configs, function(library) {
10 // Import any modules that are not overriden in the core file
1165 _.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
29125 _.each(modules, function(module, name) {
30151 var mixins;
31151 try {
32151 mixins = context.libraries.moduleMixins(module);
33 } catch (err) {
342 errored = true;
352 return complete(new Error('Failed mixins for module "' + name + '": ' + err.message));
36 }
37
38 // Map existing files that have mixin references
39149 try {
40149 ['scripts', 'styles', 'static'].forEach(function(field) {
41445 var list = module[field];
42
43445 if (list) {
44120 module[field] = context.libraries.mapFiles(list);
45 }
46 });
47
48148 _.each(mixins, function(mixin) {
4931 var mixinConfig = mixin.mixinConfig,
50 library = mixin.library;
51
52 // Direct copy for any fields that are not already defined on the object.
5331 _.defaults(module, library.attributes);
54
55 // Merge known array/object types
5631 ['scripts', 'styles', 'static', 'routes'].forEach(function(field) {
57124 mergeValues(module, field, library, mixinConfig, context);
58 });
59 });
60 } catch (err) {
611 errored = true;
621 return complete(err);
63 }
64 });
65
66 // Remove suppressed modules completely
67125 _.each(_.keys(modules), function(name) {
68151 if (!modules[name]) {
691 delete modules[name];
70 }
71 });
72
73125 if (!errored) {
74122 next(complete);
75 }
76 }
77};
78
791function firstLocal(collection) {
8042 for (var i = 0, len = collection.length; i < len; i++) {
8157 if (!collection[i].global) {
8242 return i;
83 }
84 }
850 return i;
86}
87
881function mergeValues(module, field, library, mixinConfig, context) {
89124 var value = module[field],
90 mixinValue = library.attributes[field];
91
92124 if (!value) {
9384 return;
94 }
95
9640 if (value === mixinValue) {
97 // Clone any direct copy entries from a mixin
9813 if (_.isArray(value)) {
9911 module[field] = context.libraries.mapFiles(value, library, mixinConfig);
100 } else {
1012 module[field] = _.clone(value);
102 }
10327 } else if (!_.isArray(value)) {
1045 _.defaults(value, mixinValue);
10522 } else if (mixinValue) {
10621 mixinValue = context.libraries.mapFiles(mixinValue, library, mixinConfig);
107
10821 var mixinFirstLocal = firstLocal(mixinValue),
109 moduleFirstLocal = firstLocal(value);
110
11121 if (mixinFirstLocal) {
1124 value.unshift.apply(value, mixinValue.slice(0, mixinFirstLocal));
113 }
11421 if (mixinFirstLocal < mixinValue.length) {
11521 var locals = mixinValue.slice(mixinFirstLocal);
11621 locals.unshift(mixinFirstLocal + moduleFirstLocal, 0);
11721 value.splice.apply(value, locals);
118 }
119 }
120}
121

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

87%
124
108
16
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 handlebars = require('handlebars'),
4 fs = require('fs'),
5 path = require('path'),
6 dirname = path.dirname;
7
81var moduleMapTemplate;
9
101function getModuleMapTemplate() {
1117 if (!moduleMapTemplate) {
121 moduleMapTemplate = handlebars.compile(fs.readFileSync(__dirname + '/module-map.handlebars').toString());
13 }
1417 return moduleMapTemplate;
15}
16
17// Force template load before EMFILE may be an issue
181getModuleMapTemplate();
19
201function loadModuleMap(map, mapper, callback) {
2116 var moduleMapTemplate = getModuleMapTemplate();
22
23 // This bit of voodoo forces uniform ordering for the output under node. This is used primarily for
24 // testing purposes.
2516 map = (function orderObject(map) {
2694 var ret = _.isArray(map) ? [] : {};
2794 _.keys(map).sort().forEach(function(key) {
28167 var value = map[key];
29167 ret[key] = _.isObject(value) ? orderObject(value) : value;
30 });
3194 return ret;
32 })(map);
33
3416 callback(
35 undefined,
36 moduleMapTemplate({
37 moduleMapper: mapper,
38 map: JSON.stringify(map)
39 })
40 );
41}
42
431function buildMap(context, callback) {
4434 if (context.combined) {
4515 moduleConfig(context, undefined, function(err, config, prefix) {
4615 callback(err, { base: config }, prefix);
47 });
48 } else {
4919 var attr = context.config.attributes || {},
50 app = attr.application || {},
51 modules = context.config.moduleList(context.package);
52
5319 var map = {modules: {}, routes: {}},
54 commonPrefix;
55
5619 async.forEach(modules, function(module, callback) {
5726 moduleConfig(context, module, function(err, config, prefix) {
5826 if (err) {
590 return callback(err);
60 }
61
6226 if (app.module === module) {
634 map.base = config;
64 } else {
6522 map.modules[module] = config;
66
6722 var routes = context.config.routeList(module);
6822 _.each(routes, function(value, route) {
6917 map.routes[route] = module;
70 });
71 }
7226 commonPrefix = findPrefix(prefix, commonPrefix);
73
7426 callback();
75 });
76 },
77 function(err) {
7819 callback(err, map, commonPrefix);
79 });
80 }
81}
82
831function stripPrefix(map, prefix) {
8416 if (!prefix) {
8515 return;
86 }
87
881 function stripModule(module) {
890 if (module.js) {
900 module.js = stripList(module.js);
91 }
920 if (module.css) {
930 module.css = stripList(module.css);
94 }
95 }
961 function stripList(list) {
970 if (_.isArray(list)) {
980 return list.map(stripEntry);
99 } else {
1000 return stripEntry(list);
101 }
102 }
1031 function stripEntry(entry) {
1040 if (entry.href) {
1050 entry.href = entry.href.substring(prefix.length);
1060 return entry;
107 } else {
1080 return entry.substring(prefix.length);
109 }
110 }
1111 if (map.base) {
1120 stripModule(map.base);
113 }
1141 if (map.modules) {
1150 _.each(map.modules, stripModule);
116 }
117}
1181function moduleConfig(context, module, callback) {
11941 var ret = {},
120 commonPrefix,
121 preload = module && context.config.module(module).preload,
122 depends = module && context.config.module(module).depends;
12341 if (preload) {
1241 ret.preload = preload;
125 }
12641 if (depends) {
1274 ret.depends = depends;
128 }
12941 async.forEach([{key: 'js', mode: 'scripts'}, {key: 'css', mode: 'styles'}], function(obj, callback) {
13082 fileList(context, obj.mode, module, function(err, list, prefix) {
13182 ret[obj.key] = list;
13282 commonPrefix = findPrefix(prefix, commonPrefix);
13382 callback(err);
134 });
135 },
136 function(err) {
13741 callback(err, ret, commonPrefix);
138 });
139}
1401function fileList(context, mode, module, callback) {
141 // Check to see if we even have this type of resource
14282 var modules = !context.combined ? [ module ] : context.config.moduleList(context.package);
14382 async.some(modules, function(module, callback) {
144112 var resourceContext = context.clone();
145112 resourceContext.mode = mode;
146112 resourceContext.module = context.config.module(module);
147112 resourceContext.isModuleMap = true;
148
149112 resourceContext.plugins.moduleResources(resourceContext, function(err, resources) {
150112 callback((resources || []).length);
151 });
152 },
153 function(hasResource) {
15482 if (!hasResource) {
1559 return callback();
156 }
157
158 // Output the config
15973 context.fileNamesForModule(mode, module, function(err, configs) {
16073 if (err) {
1610 return callback(err);
162 }
163
16473 var prefix;
165172 configs = configs.filter(function(config) { return !config.server; });
16699 configs = configs.sort(function(a, b) { return a.pixelDensity - b.pixelDensity; });
16773 configs = configs.map(function(config, i) {
16899 var path = config.fileName.path,
169 ret = path + '.' + config.fileName.extension;
170
17199 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
18299 prefix = findPrefix(path, prefix);
183
18499 return ret;
185 });
186
18773 var ret;
18873 if (configs.length === 1) {
18948 ret = configs[0];
19025 } else if (configs.length) {
19125 ret = configs;
192 }
19373 callback(undefined, ret, prefix);
194 });
195 });
196}
197
1981function findPrefix(path, prefix) {
199 /*jshint eqnull:true*/
200207 if (path == null) {
2019 return prefix;
202 }
203198 if (prefix == null) {
204 // Ensure that we get 'x' for strings of type 'x/'
205133 prefix = dirname(path + 'a') + '/';
206 }
207198 for (var i = 0, len = prefix.length; i < len; i++) {
208133 if (path.charAt(i) !== prefix.charAt(i)) {
209133 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) {
222243 var config = context.config;
223
224243 if (context.resource['module-map']) {
22521 var buildModuleMap = function(context, callback) {
22616 module.exports.buildMap(context, function(err, map, prefix) {
22716 if (err) {
2280 callback(err);
229 } else {
23016 var moduleMap = config.attributes.moduleMap || 'module.exports.moduleMap';
23116 stripPrefix(map, prefix);
23216 loadModuleMap(map, moduleMap, function(err, data) {
23316 callback(err, data && {data: data, generated: true, noSeparator: true, ignoreWarnings: true});
234 });
235 }
236 });
237 };
23821 buildModuleMap.sourceFile = undefined;
23921 complete(undefined, buildModuleMap);
240 } else {
241222 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) {
32281 var resource = context.resource;
33
34281 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 {
47264 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) {
21192 next(function(err, ret) {
22192 if (err) {
230 return complete(err);
24 }
25
26 // Generate the router if we have the info for it
27192 var module = context.module;
28192 if (module.routes) {
2947 ret.unshift({ routes: module.routes });
30 }
31
32192 complete(undefined, ret);
33 });
34 },
35 resource: function(context, next, complete) {
36264 var resource = context.resource,
37 module = context.module.name;
38
39264 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 {
49243 next(complete);
50 }
51 }
52};
53

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

94%
107
101
6
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) {
28412 return (attr.scope && attr.scope.scope) || attr.scope;
29}
301function toObj(obj) {
3182 return _.isString(obj) ? {scope: obj} : obj;
32}
33
341function generator(string) {
35166 var ret = function(context, callback) { callback(undefined, {data: string, generated: true, noSeparator: true}); };
3696 ret.stringValue = string;
3796 ret.sourceFile = undefined;
3896 ret.ignoreWarnings = true;
3996 return ret;
40}
41
421var scopeTemplateDelimiter = /\{?\{\{yield\}\}\}?/;
43
441function ensureModuleTemplates(context, complete) {
4546 if (!context.configCache.moduleTemplate) {
4629 var template = context.config.attributes.scope && context.config.attributes.scope.template;
4729 if (!template) {
4818 template = __dirname + '/scope-module.handlebars';
49 }
50
5129 context.fileUtil.loadTemplate(template, scopeTemplateDelimiter, function(err, templates) {
5229 if (err) {
530 complete(err);
54 } else {
5529 context.configCache.moduleTemplate = {
56 start: templates[0],
57 end: templates[1]
58 };
5929 complete();
60 }
61 });
62 } else {
6317 complete();
64 }
65}
66
671function wrapResources(resources, context) {
6846 var cache = context.moduleCache;
6946 if (!cache.scopeName) {
7046 var app = context.config.attributes.application,
71 appName = app && app.name;
72
7346 if (!appName || context.module.topLevelName || context.config.isAppModule(context.module)) {
7432 cache.isTopNamespace = true;
7532 cache.scopeName = context.module.topLevelName || appName || context.module.name;
76 } else {
7714 cache.scopeName = appName + "['" + context.module.name + "']";
78 }
7946 cache.appName = appName;
80 }
81
82 // Wrap the module content in a javascript module
8346 if (resources.length) {
8446 function isModule(reference) {
8526 var stripOperators = /['"\]]/g;
8626 return reference === cache.scopeName
87 || (!cache.isTopNamespace
88 && reference.replace(stripOperators, '').substr(-context.module.name.length) === context.module.name);
89 }
90
9146 var scope = context.config.attributes.scope || {},
92
93 // Call args calculation
94 aliasesHash = context.module.aliases === false ? {} : _.extend({}, scope.aliases, context.module.aliases),
95 aliases = _.pairs(aliasesHash),
9616 aliases = _.filter(aliases, function(alias) { return alias[1]; }),
9713 externals = _.filter(aliases, function(alias) { return !isModule(alias[1]); }),
98 aliasVars = _.pluck(externals, '0'),
99 callSpec = _.pluck(externals, '1'),
100
101 // Internal scope calculation
10213 internals = _.filter(aliases, function(alias) { return isModule(alias[1]); }),
103 internalVars = _.pluck(internals, '0'),
104 internalScope = '';
105
10646 callSpec.unshift('this');
10746 if (cache.isTopNamespace) {
10832 internalVars.unshift(cache.scopeName);
109 } else {
11014 internalScope += cache.scopeName + ' = exports;';
111 }
11284 internalVars = _.map(internalVars, function(name) { return name + ' = exports'; });
11346 if (internalVars.length) {
11433 internalScope += 'var ' + internalVars.join(', ') + ';';
115 }
116
11746 var scopeDecl = '';
11846 if (context.moduleCache.isTopNamespace) {
119 // Insert the package declaration
12032 scopeDecl = 'var ' + context.moduleCache.scopeName + ';';
121 }
12246 var templateContext = {
123 isTopNamespace: cache.isTopNamespace,
124 name: cache.appName,
125 scopeDecl: scopeDecl,
126 scope: cache.scopeName,
127 aliasVars: aliasVars.join(', '),
128 internalScope: internalScope,
129 callSpec: callSpec.join(', ')
130 };
131
13246 resources.unshift(generator(context.configCache.moduleTemplate.start(templateContext)));
13346 resources.push(generator(context.configCache.moduleTemplate.end(templateContext)));
134 }
13546 return resources;
136}
137
1381module.exports = {
139 mode: 'scripts',
140 priority: 50,
141
142 loadMixin: function(context, next, complete) {
14370 var mixinScope = toObj(context.loadedLibrary.scope);
14470 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 }
16970 next(complete);
170 },
171 loadConfig: function(context, next, complete) {
172122 var modules = context.config.attributes.modules;
173
174122 try {
175122 _.each(modules, function(module) {
176145 var mixins = context.libraries.moduleMixins(module);
177
178145 _.each(mixins, function(mixin) {
17931 context.libraries.mergeHash('aliases', module, mixin.library.attributes, module);
180 });
181 });
182 } catch (err) {
1830 return complete(err);
184 }
185
186122 next(complete);
187 },
188
189 resourceList: function(context, next, complete) {
190309 next(function(err, resources) {
191309 if (err) {
1920 return complete(err);
193 }
194
195309 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 }
201309 complete(undefined, resources);
202 });
203 },
204
205 module: function(context, next, complete) {
206103 next(function(err) {
207103 if (err) {
2080 return complete(err);
209 }
210
211103 var resources = context.moduleResources,
212 scope = getScope(context.config.attributes);
213
214103 if (resources.length && scope !== 'none') {
21546 ensureModuleTemplates(context, function(err) {
21646 if (err) {
2170 complete(err);
218 } else {
219 // Split up globals and non-globals
22046 var globals = [],
221 children = [],
222 moduleStart = [];
22346 for (var i = 0; i < resources.length; i++) {
224158 var resource = resources[i];
225158 if (resource.moduleStart) {
22613 moduleStart.push(resource);
227145 } else if (!resource.global) {
228130 children.push(resource);
229 } else {
23015 if (children.length) {
2310 throw new Error('Scoped files may not appear before global files.');
232 }
23315 globals.push(resource);
234 }
235 }
236
23746 children = moduleStart.concat(children);
23846 globals.push.apply(globals, wrapResources(children, context, complete));
239
24046 context.moduleResources = globals;
24146 complete();
242 }
243 });
244 } else {
24557 complete();
246 }
247 });
248 }
249};
250
251

/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) {
6136 return /\.(js|json)$/;
7 },
8
9 fileName: function(context, next, complete) {
10107 complete(undefined, {path: context.baseName, extension: 'js'});
11 },
12
13 moduleResources: function(context, next, complete) {
14192 var module = context.module;
15192 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) {
876 next(function(err) {
976 async.forEach(context.moduleResources, function(resource, callback) {
1028 var fileContext = context.clone();
1128 fileContext.resource = resource;
12
13 // Filter out dir entries
1428 if (resource.dir) {
152 return callback();
16 }
17
1826 fileContext.outputFile(function(callback) {
1926 var fileInfo = fileContext.loadResource(resource, function(err, data) {
2026 if (err || !data || !data.content) {
210 return callback(err);
22 }
23
2426 var ret = {
25 fileName: fileContext.fileName,
26 inputs: fileInfo.inputs || [ fileInfo.name ],
27 module: context.module.name,
28 resource: resource
29 };
30
3126 context.fileUtil.writeFile(fileContext.fileName, data.content, function(err) {
3226 if (err) {
330 err = new Error('Static output "' + fileContext.fileName + '" failed\n\t' + err);
34 }
35
3626 callback(err, ret);
37 });
38 });
39 },
40 callback);
41 },
42 complete);
43 });
44 }
45};
46

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

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

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

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

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

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

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

94%
39
37
2
LineHitsSource
11var _ = require('underscore'),
2 async = require('async');
3
41module.exports = {
5 mode: ['scripts', 'styles'],
6 priority: 25,
7
8 loadMixin: function(context, next, complete) {
970 var mixinStyles = context.loadedLibrary.styles;
1070 if (mixinStyles) {
1118 var styles = context.libraries.originalConfig.styles || {},
12 configStyles = _.clone(context.config.attributes.styles || styles),
13 assigned = false;
14
1518 ['configObject'].forEach(function(key) {
1618 if ((key in mixinStyles) && !(key in styles)) {
171 configStyles[key] = mixinStyles[key];
18
191 assigned = true;
20 }
21 });
22
2318 if (context.libraries.mergeFiles('config', styles, mixinStyles, configStyles, context.loadedLibrary)) {
244 assigned = true;
25 }
26
2718 if (assigned) {
284 context.config.attributes.styles = configStyles;
29 }
30 }
3170 next(complete);
32 },
33
34 resource: function(context, next, complete) {
35412 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 {
70409 next(complete);
71 }
72 },
73
74 module: function(context, next, complete) {
75199 next(function() {
76199 var styles = context.config.attributes.styles || {},
77 config = styles.config || [];
78
79199 if (config.length) {
8059 _.each(context.moduleResources, function(resource) {
81218 if (resource.stylus) {
8229 resource.plugins.push({
83 plugin: __dirname + '/stylus-config-worker',
84 data: config
85 });
86 }
87 });
88 }
89
90199 complete();
91 });
92 }
93};
94

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

95%
124
118
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) {
31253 worker.sendAll({type: 'cache:reset', path: path});
32});
33
341var worker = new ChildPool(__dirname + '/stylus-worker');
35
361function generateSource(context, options, styleConfig) {
3736 var includes = (styleConfig.includes || []).concat(options.files),
38 module = options.module;
39
4036 var nibLocation = includes.indexOf('nib'),
41 useNib;
4236 if (styleConfig.useNib) {
4331 useNib = true;
4431 includes.unshift('nib');
455 } 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
5236 var declare = context.config.platformList().map(function(platform) {
5371 return '$' + platform + ' = ' + (platform === context.platform);
54 }).join('\n') + '\n';
55
5636 var mixins = [],
57 mixinLUT = {};
58
5936 var source = declare + includes.map(function(include) {
60113 var source = include.library;
61113 var statement = '@import ("' + (include.originalSrc || include.src || include) + '")\n';
62113 if (source) {
638 var name = '',
64 root = (source.parent || source).root || '',
65 stylusRoot = ((source.parent || source).styles || {}).styleRoot,
66 library = (source.parent || source).name || '';
678 if (source.parent) {
687 name = source.name || '';
69 }
708 var mixinName = name + '_' + library;
71
728 if (!mixinLUT[mixinName]) {
735 var mixinDecl = context.libraries.findDecl(module.mixins, {name: name, library: library}),
74 overrides = mixinDecl && mixinDecl.overrides;
75
765 mixins.push({
77 root: normalize(root),
78 stylusRoot: stylusRoot && normalize(stylusRoot),
79 overrides: overrides
80 });
815 mixinLUT[mixinName] = mixins.length-1;
82 }
838 mixinName = mixinLUT[mixinName];
84
858 return 'push-mixin("' + mixinName + '")\n'
86 + statement
87 + 'pop-mixin()\n';
88 } else {
89105 return statement;
90 }
91 }).join('');
92
9336 return {
94 useNib: useNib,
95 source: source,
96 mixins: mixins
97 };
98}
99
1001function compile(options, callback) {
10136 var context = options.context,
102
103 styleConfig = context.config.attributes.styles || {};
104
10536 var loadPrefix = context.config.loadPrefix(),
106 externalPrefix;
10736 if (loadPrefix) {
1089 externalPrefix = loadPrefix + (context.buildPath.indexOf('/') >= 0 ? path.dirname(context.buildPath) + '/' : '');
109 }
110
11136 var imageOptions = {
112 outdir: path.dirname(context.fileName),
113 resolutions: context.modeCache.pixelDensity,
114 limit: styleConfig.urlSizeLimit,
115 copyFiles: styleConfig.copyFiles,
116 externalPrefix: externalPrefix
117 };
118
11936 var source = generateSource(context, options, styleConfig);
120
12136 context.fileUtil.ensureDirs(context.fileName, function(err) {
12236 if (err) {
1230 return callback(err);
124 }
125
12636 worker.send({
127 plugins: options.plugins,
128
129 useNib: source.useNib,
130 imageOptions: imageOptions,
131
132 filename: options.filename,
133 minimize: context.options.minimize,
134
135 source: source.source,
136 mixins: source.mixins,
137
138 lookupPath: context.fileUtil.lookupPath(),
139 styleRoot: styleConfig.styleRoot && context.fileUtil.resolvePath(styleConfig.styleRoot)
140 },
141 callback);
142 });
143}
144
1451module.exports = {
146 // scripts mode is used also to support inline styles
147 mode: ['styles', 'scripts'],
148 priority: 50,
149
150 loadMixin: function(context, next, complete) {
15170 var mixinStyles = context.loadedLibrary.styles;
15270 if (mixinStyles) {
15318 var styles = context.libraries.originalConfig.styles || {},
154 configStyles = _.clone(context.config.attributes.styles || styles),
155 assigned = false;
156
15718 ['urlSizeLimit', 'copyFiles', 'useNib'].forEach(function(key) {
15854 if ((key in mixinStyles) && !(key in styles)) {
1598 configStyles[key] = mixinStyles[key];
160
1618 assigned = true;
162 }
163 });
164
16518 if (context.libraries.mergeFiles('includes', styles, mixinStyles, configStyles, context.loadedLibrary)) {
1664 assigned = true;
167 }
168
16918 if (context.libraries.mergeHash('pixelDensity', styles, mixinStyles, configStyles)) {
1703 assigned = true;
171 }
172
17318 if (assigned) {
1748 context.config.attributes.styles = configStyles;
175 }
176 }
17770 next(complete);
178 },
179
180 outputConfigs: function(context, next, complete) {
181191 if (!inlineStyles.isInline(context) && context.mode !== 'styles') {
18291 return next(complete);
183 }
184
185100 next(function(err, files) {
186100 if (err) {
1870 return complete(err);
188 }
189
190100 var ret = [],
191 styleConfig = context.config.attributes.styles || {},
192 pixelDensity = styleConfig.pixelDensity || {};
193100 if (context.platform) {
19474 pixelDensity = pixelDensity[context.platform] || pixelDensity;
195 }
196100 if (!_.isArray(pixelDensity)) {
19763 pixelDensity = [ 1 ];
198 }
199100 context.modeCache.pixelDensity = pixelDensity;
200
201 // Permutation of other configs and ours
202100 var primary = true;
203100 files.forEach(function(fileConfig) {
204100 pixelDensity.forEach(function(density) {
205138 var config = _.clone(fileConfig);
206138 config.pixelDensity = density;
207138 config.isPrimary = primary;
208138 primary = false;
209138 ret.push(config);
210 });
211 });
212100 complete(undefined, ret);
213 });
214 },
215
216 fileName: function(context, next, complete) {
217229 if (!inlineStyles.isInline(context) && context.mode !== 'styles') {
218101 return next(complete);
219 }
220
221128 next(function(err, ret) {
222128 if (ret && context.fileConfig.pixelDensity !== 1) {
22340 ret.path += '@' + context.fileConfig.pixelDensity + 'x';
224 }
225128 complete(err, ret);
226 });
227 },
228
229 module: function(moduleContext, next, complete) {
230201 next(function(err) {
231 /*jshint eqnull: true */
232201 if (err) {
2330 return complete(err);
234 }
235
236201 function mergeResources(start) {
23754 var generator = function(context, callback) {
23854 function response(data, density) {
23954 if (data) {
24051 return {
241 data: data.data[density || 1],
242 inputs: data.inputs,
243 noSeparator: true
244 };
245 }
246 }
247
24854 var filename = generator.filename;
249
250 // We only want to call stylus once which will generate the css for all of the
251 // resolutions we support on this platform. This ugly bit of code make sure that
252 // we properly handle all of that loading states that can come into play under these
253 // circumstances while still adhering to the output models prescribed by lumbar.
25454 var queue = context.modeCache['stylus_' + filename];
25554 if (_.isArray(queue)) {
256 // We are currently executing
25718 queue.push({density: context.fileConfig.pixelDensity, callback: callback});
25836 } else if (_.isObject(queue)) {
259 // We already have data
2600 callback(undefined, response(queue, context.fileConfig.pixelDensity));
261 } else {
262 // We need to kick of a stylus build
26336 queue = context.modeCache['stylus_' + filename] = [
264 {density: context.fileConfig.pixelDensity, callback: callback}
265 ];
26636 var options = {
267 filename: filename,
268 files: generator.inputs,
269
270 context: context,
271 module: moduleContext.module, // To play nicely with combined mode
272 plugins: generator.plugins
273 };
27436 compile(options, function(err, data) {
27536 if (err) {
2763 data = undefined;
277 }
27836 _.each(queue, function(callback) {
27954 callback.callback(err, response(data, callback.density));
280 });
28136 context.modeCache['stylus_' + filename] = data;
282 });
283 }
284 };
28554 generator.inputs = resources.splice(start, rangeEnd - start + 1);
286143 generator.filename = 'stylus_' + _.map(generator.inputs, function(file) { return file.originalSrc || file.src; }).join(';');
28754 generator.style = true;
28854 generator.stylus = true;
28954 generator.plugins = [];
290
29154 resources.splice(start, 0, generator);
29254 rangeEnd = undefined;
293 }
294
295 // Merge all consequtive stylus files together
296201 var resources = moduleContext.moduleResources,
297 len = resources.length,
298 rangeEnd;
299201 while (len--) {
300414 var resource = resources[len];
301
302414 if (/\.styl$/.test(resource.src)) {
30389 if (!rangeEnd) {
30454 rangeEnd = len;
305 }
306325 } else if (rangeEnd) {
3073 mergeResources(len + 1);
308 }
309 }
310201 if (rangeEnd != null) {
31151 mergeResources(0);
312 }
313201 complete();
314 });
315 }
316};
317

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

97%
78
76
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
251module.exports = {
26 mode: 'scripts',
27 priority: 50,
28
29 loadMixin: function(context, next, complete) {
3070 var mixinTemplates = context.loadedLibrary.templates;
3170 if (mixinTemplates) {
329 var templates = context.libraries.originalConfig.templates || {},
33 configTemplates = _.clone(context.config.attributes.templates || templates),
34 assigned = false;
35
369 if (context.libraries.mergeHash('auto-include', templates, mixinTemplates, configTemplates)) {
373 assigned = true;
38 }
39
409 if (assigned) {
413 context.config.attributes.templates = configTemplates;
42 }
43 }
4470 next(complete);
45 },
46
47 resourceList: function(context, next, complete) {
48309 var library = context.resource.library,
49 attr = (library && library.parent || library) || context.config.attributes;
50
51309 next(function(err, ret) {
52309 if (err || !ret) {
530 return complete(err);
54 }
55
56309 function pushTemplates(templates) {
57274 _.each(templates, function(template) {
5843 var src = template.src;
5943 if (!src || (template.library && !template.library.attributes)) {
6033 var templateLibrary = template.library ? context.libraries.getConfig(template.library) : library;
6133 src = context.libraries.resolvePath(template.src || template, templateLibrary);
62 }
63
6443 ret.unshift({
65 src: src,
66 name: template.name || template.src || template,
67 library: templateLibrary || template.library || library,
68 template: true
69 });
70 });
71 }
72
73309 var views = attr.templates || attr.views || {},
74 globalConfig = (context.config.attributes.templates || {}),
75 resource = context.resource.originalSrc || context.resource.src || context.resource,
76 mixinRoot = (context.resource.library && context.resource.library.root) || '';
77309 if (_.isString(resource) && resource.indexOf(mixinRoot) === 0) {
78235 resource = resource.substring(mixinRoot.length);
79 }
80
81309 var deferComplete;
82309 if (build.filterResource(context.resource, context)) {
83265 pushTemplates(views[resource]);
84
85265 if (globalConfig['auto-include']) {
8610 var config = context.configCache['template-auto-include'];
8710 if (!config) {
885 config = module.exports.generateMappings(globalConfig['auto-include']);
895 context.configCache['template-auto-include'] = config;
90 }
91
9210 var autoIncludes = module.exports.autoIncludes(resource, config, context);
9310 if (autoIncludes.length) {
949 deferComplete = true;
95
969 context.fileUtil.fileList(autoIncludes, function(err, autoIncludes) {
979 if (err) {
980 return complete(err);
99 }
100
1019 var watchDirs = [];
1029 autoIncludes = _.filter(autoIncludes, function(file) {
10313 if (file.enoent) {
1043 watchDirs.push({watch: path.dirname(file.src)});
105 } else {
10610 return true;
107 }
108 });
109
1109 if (autoIncludes.length) {
1117 context.event.emit('log', 'Autoincludes for "' + resource + '" ' + JSON.stringify(_.pluck(autoIncludes, 'src'), undefined, 2));
112 }
113
1149 pushTemplates(autoIncludes);
1159 ret.unshift.apply(ret, watchDirs);
116
1179 complete(undefined, ret);
118 });
119 }
120 }
121 }
122309 if (!deferComplete) {
123300 complete(undefined, ret);
124 }
125 });
126 },
127
128 resource: function(context, next, complete) {
129222 var resource = context.resource;
130
131222 if (resource.watch) {
1322 function generator(buildContext, callback) {
133 // Ensure that the directory actually exists
1342 var path = context.fileUtil.resolvePath(resource.watch);
1352 context.fileUtil.stat(path, function(err, stat) {
136 // Ignore any errors here
1372 var inputs = [];
1382 if (stat && stat.isDirectory()) {
1392 inputs.push(path);
140 }
1412 callback(undefined, {inputs: inputs, data: '', noSeparator: true});
142 });
143 }
1442 complete(undefined, generator);
145 } else {
146220 next(complete);
147 }
148 },
149
150 autoIncludes: function(resource, config, context) {
15110 var autoIncludes = [];
15210 _.each(config, function(mapping) {
15310 var remap = module.exports.remapFile(mapping, resource, context);
15410 if (remap) {
1559 autoIncludes.push.apply(autoIncludes, remap);
156 }
157 });
15810 return autoIncludes;
159 },
160 generateMappings: function(autoInclude) {
1615 return _.map(autoInclude, function(templates, source) {
1625 if (!_.isArray(templates)) {
1632 templates = [templates];
164 }
1655 return {regex: new RegExp(source), templates: templates};
166 });
167 },
168 remapFile: function(mapping, resource, context) {
169 /*jshint boss:true */
17013 var match;
17113 if (match = mapping.regex.exec(resource)) {
17211 return _.map(mapping.templates, function(template) {
173 // Work in reverse so $10 takes priority over $1
17421 var i = match.length;
17521 while (i--) {
17646 template = template.replace('$' + i, match[i]);
177 }
17821 return {name: template, src: context.libraries.resolvePath(template, template.library || context.resource.library)};
179 });
180 }
181 }
182};
183

/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 complete(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%
106
106
0
LineHitsSource
11var _ = require('underscore'),
2 async = require('async'),
3 build = require('./build'),
4 ChildPool = require('child-pool'),
5 combine = require('./jsCombine'),
6 configLoader = require('./config'),
7 Context = require('./context'),
8 fs = require('fs'),
9 fu = require('./fileUtil'),
10 Libraries = require('./libraries'),
11 plugin = require('./plugin'),
12 WatchManager = require('./watch-manager');
13
141exports.loadConfig = function(path, event, options, callback) {
1537 try {
1637 fu.resetCache();
17
1837 var config = _.isString(path) ? configLoader.load(path) : configLoader.create(path);
19
2037 var plugins = plugin.create(options);
2137 plugins.initialize(config);
22
2336 config.outdir = options.outdir = options.outdir || config.attributes.output;
24
2536 var libraries = new Libraries(options);
2636 var context = new Context(options, config, plugins, libraries, event);
2736 context.options = options;
2836 context.configCache = {};
29
3036 libraries.initialize(context, function(err) {
3136 if (err) {
321 return callback(err);
33 }
34
3535 plugins.loadConfig(context, function(err) {
3635 if (err) {
371 return callback(err);
38 }
39
4034 event.emit('config', context.config);
4134 if (options.verbose) {
421 event.emit('log', 'Finalized config ' + JSON.stringify(context.config.serialize(), undefined, 2));
43 }
44
45 // Ensure that we have the proper build output
4634 if (!config.outdir) {
471 return callback(new Error('Output must be defined on the command line or config file.'));
48 }
4933 context.outdir = config.outdir;
50
5133 fu.ensureDirs(config.outdir + '/.', function() {
5233 var stat = fs.statSync(config.outdir);
5333 if (!stat.isDirectory()) {
541 callback(new Error('Output must be a directory'));
55 } else {
5632 callback(undefined, context);
57 }
58 });
59 });
60 });
61 } catch (err) {
621 callback(err);
63 }
64};
65
661exports.buildPackages = function(context, packageName, modules, callback) {
6739 if (!callback) {
6814 callback = modules;
6914 modules = undefined;
70 }
71
72 // Allow a string or a list as modules input
7339 if (!_.isArray(modules)) {
7437 modules = [modules];
752 } else if (!modules.length) {
76 // Special case empty array input to build all
771 modules = [undefined];
78 }
79
8039 var options = {};
8139 if (typeof packageName === 'object') {
821 options = packageName;
831 packageName = options.package;
84 }
85
8639 var packageNames = packageName ? [packageName] : context.config.packageList(),
87 contexts = [];
88
8939 packageNames.forEach(function(pkg) {
9043 modules.forEach(function(module) {
9144 options.package = pkg;
9244 options.module = module || undefined; // '' -> undefined
93
9444 context.event.emit('debug', 'Build package: ' + pkg);
95
9644 var platforms = context.config.platformList(pkg);
9744 platforms.forEach(function(platform) {
9860 options.platform = platform;
99
10060 var newContext = context.clone(options);
10160 contexts.push(newContext);
102 });
103 });
104 });
105
10639 async.forEach(contexts, exports.buildPlatform, callback);
107};
1081exports.buildPlatform = function(context, callback) {
10984 context.event.emit('debug', 'Build platform: ' + context.description);
11084 var modes = context.mode ? [context.mode] : context.plugins.modes();
111
11284 async.forEach(modes, function(mode, callback) {
113180 exports.buildMode(mode, context, callback);
114 },
115 callback);
116};
1171exports.buildMode = function(mode, context, callback) {
118183 context.event.emit('debug', 'Build mode: ' + context.description);
119
120183 var modules = context.module ? [context.module] : context.config.moduleList(context.package);
121
122183 context = context.clone();
123183 context.mode = mode;
124183 context.modeCache = {};
125
126183 if (context.fileConfig) {
12723 processFileConfig(context.fileConfig, callback);
128 } else {
129160 context.plugins.outputConfigs(context, function(err, configs) {
130160 if (err) {
1311 return callback(err);
132 }
133159 async.forEach(configs, processFileConfig, callback);
134 });
135 }
136
137183 function processFileConfig(fileConfig, callback) {
138194 var fileContext = context.clone(true);
139194 fileContext.fileConfig = fileConfig;
140194 fileContext.resources = [];
141194 fileContext.combineResources = {};
142194 fileContext.fileCache = fileContext.combined ? {} : undefined;
143
144194 async.forEach(modules, function(module, callback) {
145283 var moduleContext = fileContext.clone();
146283 moduleContext.module = module;
147
148283 exports.buildModule(moduleContext, callback);
149 },
150 function(err) {
151190 if (err) {
1525 return callback(err);
153 }
154
155185 context.plugins.modeComplete(fileContext, callback);
156 });
157 }
158};
1591exports.buildModule = function(context, callback) {
160275 context.event.emit('debug', 'Build module: ' + context.description);
161
162275 var module = context.config.module(context.module);
163275 if (!module) {
1641 return callback(new Error('Unable to find module "' + context.module + '"'));
165 }
166
167274 context.module = module;
168274 context.fileCache = context.combined ? context.fileCache : {};
169274 context.moduleCache = {};
170
171274 var resource = context.resource;
172274 if (resource) {
1737 resource = resource.originalResource || resource;
1747 exports.processResources(context, [resource], callback);
175 } else {
176 // Load all resources associated with this module
177267 build.loadResources(context, function(err, resources) {
178267 if (err) {
1791 return callback(err);
180 }
181266 exports.processResources(context, resources, callback);
182 });
183 }
184};
185
1861exports.processResources = function(context, resources, callback) {
187273 build.processResources(resources, context, function(err, resources) {
188273 if (err) {
1891 return callback(err);
190 }
191
192272 context.moduleResources = resources;
193272 context.plugins.module(context, callback);
194 });
195};
196

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

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

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

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

90%
101
91
10
LineHitsSource
1/**
2 * Adds dependency watching to the core fs.watchFile implementation.
3 */
41var _ = require('underscore'),
5 fs = require('fs');
6
71var watchedFiles = {};
8
91function notifyWatch(filename, type, sourceChange, trigger) {
1081 var watchInfo = watchedFiles[filename];
1181 if (watchInfo) {
1278 var inQueue = _.find(watchInfo.queue, function(entry) {
130 return entry.type === type
14 && entry.filename === filename
15 && entry.sourceChange === sourceChange;
16 });
17
1878 if (!inQueue) {
1978 var entry = {type: type, filename: filename, sourceChange: sourceChange};
2078 watchInfo.queue.push(entry);
21
2278 function exec() {
2378 watchInfo.queue = _.without(watchInfo.queue, entry);
24
2578 if (watchInfo.callback) {
2652 watchInfo.callback(type, filename, sourceChange);
27 }
2878 watchInfo.parents.forEach(function(parent) {
2943 notifyWatch(parent, type, sourceChange, trigger);
30 });
31 }
32
3378 if (trigger) {
3472 exec();
35 } else {
36 // Debounce so we don't output multiple instances of the same event on platforms
37 // such as linux that may send multiple events on write, etc.
386 _.defer(exec, 200);
39 }
40 }
41 }
42}
43
441function watchFile(filename, callback, parent) {
45185 var watchInfo = {
46 callback: callback,
47 parents: [],
48 queue: []
49 };
50185 if (parent) {
51106 watchInfo.parents.push(parent);
52 }
53185 watchedFiles[filename.virtual || filename] = watchInfo;
54
55185 if (!filename.virtual) {
566 var hasRetried;
57
586 (function watch(ignoreError) {
597 try {
607 var oldStat = fs.statSync(filename),
61 lastType,
62 rewatch;
63
647 var changeHandler = _.debounce(function() {
653 if (rewatch) {
66 // Attempt to reattach on rename
671 watchInfo.watch.close();
681 watch(true);
69 }
703 notifyWatch(filename, lastType, filename);
71 }, 1000);
72
737 watchInfo.watch = fs.watch(filename, function(type) {
746 try {
756 var newStat = fs.statSync(filename);
764 if (newStat.isDirectory()) {
771 notifyWatch(filename, 'create', filename);
783 } else if (newStat.size !== oldStat.size || newStat.mtime.getTime() > oldStat.mtime.getTime()) {
793 oldStat = newStat;
803 if (type === 'rename') {
811 rewatch = true;
82 }
833 lastType = type;
843 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) {
123101 var watch = watchedFiles[filename.virtual || filename];
124101 if (!watch) {
125 // Create a watch on this and all others
12679 watchFile(filename, callback);
127 } else {
12822 watch.callback = callback;
129 }
130
131101 filename = filename.virtual || filename;
132
133101 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() {
18860 _.each(watchedFiles, function(watch, name) {
189180 unwatch(name);
190 });
191};
192
1931function unwatch(name) {
194185 watchedFiles[name].callback = undefined;
195185 if (watchedFiles[name].watch) {
1966 watchedFiles[name].watch.close();
197 }
198185 delete watchedFiles[name];
199}
200

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

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