UNPKG

8.77 kBJavaScriptView Raw
1var path = require('path');
2var fs = require('fs');
3var util = require('util');
4
5var pkg = require('../package.json');
6
7var debug = require('debug')('express-mincer-spa');
8debug.log = console.log.bind(console);
9var express = require('express');
10var _ = require('lodash');
11var favicon = require('serve-favicon');
12var ConnectMincer = require('connect-mincer');
13var Mincer = require('mincer');
14var liveReload = require('livereload');
15
16/**
17 *
18 * @param {string} root
19 * @param {Object} opts
20 * @return {Express App} app
21 */
22exports = module.exports = function(root, opts) {
23 if(_.isUndefined(root)) {
24 throw new Error('Parameter "root" have to be defined');
25 }
26
27 debug('setup express application');
28 var app = express();
29
30 app.bootstrap = function(config) {
31 debug('look for custom startup config');
32 if(config == null) {
33 config = {};
34 }
35
36 debug('setup default options');
37 var defaultConfig = exports.getDefaultConfig(root, app.get('env'));
38
39 debug('merge options to app configuration');
40 app.config = _.assign({}, defaultConfig);
41 _.merge(app.config, opts);
42 _.merge(app.config, config);
43
44 debug('scan directories and merge found asset paths');
45 _.merge(
46 app.config.assets.paths,
47 exports.scanDirectories(
48 app.config.assets.root,
49 app.config.assets.scanDirectories
50 )
51 );
52
53 // setup AssetPipeline
54 debug('setup asset pipeline');
55 app.assetPipeline = new ConnectMincer(app.config.assets);
56 if(_.isFunction(app.config.configureAssetPipeline)) {
57 app.config.configureAssetPipeline(app.assetPipeline);
58 }
59 };
60
61 app.setup = function(config) {
62 app.bootstrap(config);
63
64 // view engine
65 app.set('views', app.config.views);
66 app.set('view engine', app.config.engine);
67
68 // middlewares
69 if(_.isFunction(app.config.configureExpressBeforeMiddlewares)) {
70 app.config.configureExpressBeforeMiddlewares(app);
71 }
72 debug('setup express static for public folder');
73 app.use(app.config.middlewareForStaticPublic, express.static(app.config.public));
74 debug('setup favicon');
75 app.use(favicon(app.config.favicon));
76
77 app.use(app.assetPipeline.assets());
78
79 if(app.get('env') !== 'production') {
80 app.use(app.config.assets.mountPath, app.assetPipeline.createServer());
81 exports.livereload(app);
82 }
83
84 if(_.isFunction(app.config.configureExpress)) {
85 app.config.configureExpress(app);
86 }
87
88 app.use('/*', function(req, res) {
89 res.render('index');
90 });
91
92 app.use(function(err, req, res, next) {
93 var resp = {
94 message: err.message
95 };
96 if(app.get('env') !== 'production') {
97 resp.error = err;
98 resp.stack = err.stack.split('\n');
99 }
100 res.json(resp);
101 });
102 };
103
104 app.start = function(config, done) {
105 if(done == null) {
106 done = function(err) {
107 if(err) {
108 throw err;
109 }
110
111 console.log("Application running on %s", app.config.host());
112 };
113 }
114
115 app.setup(config);
116 app.listen(app.config.port, app.config.ip, done);
117 };
118
119 return app;
120};
121
122exports.getDefaultConfig = function(root, env) {
123 return {
124 port: process.env.PORT || 3000,
125 ip: process.env.IP || '',
126
127 host: function() {
128 return exports.host(this.ip, this.port);
129 },
130
131 engine: 'jade',
132 views: path.resolve(root, 'views'),
133 public: path.resolve(root, 'public'),
134 favicon: path.resolve(root, 'public', 'favicon.ico'),
135
136 middlewareForStaticPublic: [],
137 configureAssetPipeline: function(assetPipeline) {},
138 configureExpressBeforeMiddlewares: function(app) {},
139 configureExpress: function(app) {},
140
141 assets: {
142 mincer: Mincer,
143 root: root,
144 production: env === 'production',
145 mountPath: '/assets',
146 manifestFile: path.resolve(root, 'public', 'assets', 'manifest.json'),
147 paths: [],
148 scanDirectories: [
149 'assets'
150 ]
151 },
152
153 precompile: {
154 target: path.resolve(root, 'public', 'assets'),
155 images: true,
156 fonts: true,
157 files: [],
158 options: {}
159 },
160
161 livereload: {
162 active: env === 'development',
163 ip: process.env.LIVERELOAD_IP || 'localhost',
164 port: process.env.LIVERELOAD_PORT || 35729,
165 script: function () {
166 return util.format('//%s:%s/livereload.js', this.ip, this.port);
167 },
168 host: function() {
169 return exports.host(this.ip, this.port);
170 },
171 watch: [],
172 debug: env === 'development',
173 exts: [
174 'js',
175 'coffee',
176 'json',
177 'html',
178 'jade',
179 'ejs',
180 'css',
181 'styl',
182 'less',
183 'png',
184 'gif',
185 'jpg',
186 'svg',
187 'ico',
188 'eof',
189 'ttf',
190 'woff',
191 'woff2'
192 ]
193 }
194 };
195};
196
197/**
198 *
199 * @param {string} root
200 * @param {Array} directories
201 * @return {Array} asset paths
202 */
203exports.scanDirectories = function(root, directories) {
204 var results = [];
205 directories.forEach(function(directory) {
206 var directoryPath = path.resolve(root, directory);
207 if(fs.existsSync(directoryPath)) {
208 fs.readdirSync(directoryPath).forEach(function(file) {
209 var stat = fs.lstatSync(path.resolve(directoryPath, file));
210 if(stat.isDirectory()) {
211 results.push(path.join(directory, file));
212 }
213 });
214 } else {
215 debug('Directory "%s" does not exist', directory);
216 }
217 });
218
219 return results;
220};
221
222exports.livereload = function (app) {
223 if(app.config.livereload.active) {
224 debug('enable livereload server');
225 // Bind LiveReload Server
226 app.liveReloadServer = liveReload.createServer(_.merge({}, app.config.livereload));
227
228 // Override Debug Logger
229 // const _debug = app.liveReloadServer.debug;
230 app.liveReloadServer.debug = function (str) {
231 debug("LiveReload Server:", str);
232 };
233
234 // Load Watch Directories
235 var watch = app.config.livereload.watch;
236 exports.arrayPush(watch, app.get('views') + "/*");
237 exports.arrayPush(watch, app.config.assets.paths, function(p) {
238 return path.resolve(app.config.assets.root, p);
239 });
240
241 debug("LiveReload enabled.");
242 debug("Watching:", watch);
243 debug("LiveReload Server on", app.config.livereload.host());
244 // Start Server
245 app.liveReloadServer.watch(watch);
246
247 // Bind LRScript Middleware
248 app.use(function(req, res, next) {
249 res.locals.LRScriptFile = app.config.livereload.script();
250 res.locals.LRScriptTag = "<script src=\""+res.locals.LRScriptFile+"\"></'+'script>";
251 res.locals.LRScript = "<script>document.write('"+res.locals.LRScriptTag+"')</script>";
252 next();
253 });
254 } else {
255 debug('disable livereload server');
256 app.use(function (req, res, next) {
257 res.locals.LRScript = "<!-- LiveReload disabled -->";
258 next();
259 });
260 }
261};
262
263exports.arrayPush = function (target, item, transform) {
264 if (_.isUndefined(transform)) {
265 transform = function(x) { return x; };
266 }
267 item = _.isArray(item) ? item : [item];
268
269 item.forEach(function (p) {
270 target.push(transform(p));
271 });
272
273 return target;
274};
275
276exports.host = function(ip, port) {
277 if(_.isEmpty(ip) === false) {
278 return util.format('%s:%s', ip, port);
279 }
280
281 return util.format('localhost:%s', port);
282};
283
284exports.getPrecompileFonts = function() {
285 return [
286 '*.eot',
287 '*.svg',
288 '*.ttf',
289 '*.woff',
290 '*.woff2',
291 '**/*.eot',
292 '**/*.svg',
293 '**/*.ttf',
294 '**/*.woff',
295 '**/*.woff2'
296 ];
297};
298
299exports.getPrecompileImages = function() {
300 return [
301 '*.png',
302 '*.gif',
303 '*.jpg',
304 '*.ico',
305 '**/*.png',
306 '**/*.gif',
307 '**/*.jpg',
308 '**/*.ico'
309 ];
310};
311
312exports.pkg = pkg;