UNPKG

6.4 kBJavaScriptView Raw
1var _ = require('underscore'),
2 async = require('async'),
3 ChildPool = require('child-pool'),
4 Context = require('./context'),
5 EventEmitter = require('events').EventEmitter,
6 fs = require('fs'),
7 stateMachine = require('./state-machine'),
8 WatchManager = require('./watch-manager');
9
10exports.build = require('./build');
11exports.fileUtil = require('./fileUtil');
12exports.plugin = require('./plugin').plugin;
13exports.combine = require('./jsCombine').combine;
14exports.config = require('./config');
15
16/**
17 *
18 * @name init
19 * @function This function initializes a Lumbar instance
20 * @param {string} lumbarFile The lumbarFile is the main
21 * file. Its responsible to define all the platforms,
22 * packages, modules, and templates for Lumbar to use.
23 * @param {Object} options supports the following options:
24 * packageConfigFile (string): name of the package config file.
25 * outdir (string): path to directory of where to output the files.
26 * minimize (boolean): Should we minimize the files?
27 * @return {Object.<Function>}
28 */
29exports.init = function(lumbarFile, options) {
30 // Clone so we can mutate in the use API
31 options = _.clone(options || {});
32 options.plugins = _.clone(options.plugins || []);
33
34 function logError(err) {
35 if (err) {
36 event.emit('error', err);
37 }
38 }
39
40 var event = new EventEmitter(),
41 watch,
42 watchContext;
43
44 function watchOutputHandler(status) {
45 if (!watch) {
46 // We've been cleaned up but residuals may still exist, do nothing on this exec
47 return;
48 }
49
50 if (status.fileConfig.isPrimary) {
51 delete status.fileConfig;
52 } else if (status.fileConfig.isPrimary === false) {
53 // This config is directly linked to another meaning we don't want to watch on it as
54 // it will be rebuilt.
55 return;
56 }
57
58 var originalContext = watchContext;
59 watch.moduleOutput(status, function() {
60 if (watchContext !== originalContext) {
61 // Ignore builds that may have occured at the same time as a config file change (i.e. a branch switch)
62 return;
63 }
64
65 stateMachine.loadPlatform(watchContext.clone(status), function(err, contexts) {
66 if (err) {
67 return logError(err);
68 }
69
70 stateMachine.buildContexts(contexts, logError);
71 });
72 });
73 }
74
75 return _.extend(event, {
76 use: function(plugin) {
77 // Only has impact before exec
78 options.plugins.push(plugin);
79 },
80
81 moduleMap: function(packageName, callback) {
82 if (!callback) {
83 callback = packageName;
84 packageName = undefined;
85 }
86
87 stateMachine.loadConfig(lumbarFile, event, options, function(err, rootContext) {
88 if (err) {
89 return callback(err);
90 }
91
92 rootContext.mode = 'scripts';
93 stateMachine.loadPackages(rootContext, packageName, function(err, contexts) {
94 if (err) {
95 return callback(err);
96 }
97
98 async.forEach(_.keys(contexts), function(packageName, callback) {
99 var package = contexts[packageName];
100 async.forEach(_.keys(package), function(platformName, callback) {
101 var platform = package[platformName],
102 context = platform[0];
103
104 rootContext.plugins.get('module-map').buildMap(context, function(err, map) {
105 if (!err) {
106 package[platformName] = map;
107 }
108 callback(err);
109 });
110 },
111 callback);
112 },
113 function(err) {
114 callback(err, contexts);
115 });
116 });
117 });
118 },
119
120 /**
121 *
122 * @name build
123 * @function This function builds out the package(s).
124 * @param {string} packageName the name of the package listed under
125 * 'packages' from the lumbarFile passed in during the call to init().
126 * @param {Function} callback the node process Function
127 */
128 build: function(packageName, modules, callback) {
129 stateMachine.loadAndInitDir(lumbarFile, event, options, function(err, rootContext) {
130 if (err) {
131 if (!callback) {
132 throw err;
133 }
134 return callback(err);
135 }
136
137 stateMachine.buildPackages(rootContext, packageName, modules, callback);
138 });
139 },
140 watch: function(packageName, modules, callback) {
141 if (!fs.watch) {
142 throw new Error('Watch requires fs.watch, introduced in Node v0.6.0');
143 }
144
145 ChildPool.isBackground(true);
146
147 watch = new WatchManager();
148 watch.on('watch-change', function(info) {
149 event.emit('watch-change', info);
150 });
151
152 var self = this;
153 stateMachine.loadAndInitDir(lumbarFile, event, options, function(err, rootContext) {
154 if (err) {
155 logError(err);
156 }
157
158 if (!callback) {
159 callback = modules;
160 modules = undefined;
161 }
162
163 watchContext = rootContext;
164
165 // Watch for changes in the config file
166 var mixinPaths = _.filter(_.pluck(rootContext.libraries.configs, 'path'), function(path) { return path; });
167 watch.configFile(lumbarFile, mixinPaths, function() {
168 watchContext = undefined;
169 self.watch(packageName, callback);
170 });
171
172 // If we have errored do not exec everything as it could be in an indeterminate state
173 if (err) {
174 return;
175 }
176
177 // Watch the individual components
178 event.removeListener('output', watchOutputHandler);
179 event.on('output', watchOutputHandler);
180
181 // Actual build everything
182 var packages = packageName ? [packageName] : rootContext.config.packageList();
183 packages.forEach(function(name) {
184 stateMachine.buildPackages(rootContext, name, modules, logError);
185 });
186 });
187 },
188 unwatch: function() {
189 event.removeListener('output', watchOutputHandler);
190 if (watch) {
191 watch.removeAllListeners();
192 watch.reset();
193 watch = undefined;
194 watchContext = undefined;
195 }
196 }
197 });
198};