UNPKG

8.55 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
69 // middlewares
70 debug('setup express static for public folder');
71 app.use(app.config.middlewareForStaticPublic, express.static(app.config.public));
72 debug('setup favicon');
73 app.use(favicon(app.config.favicon));
74
75 app.use(app.assetPipeline.assets());
76
77 if(app.get('env') !== 'production') {
78 app.use(app.config.assets.mountPath, app.assetPipeline.createServer());
79 exports.livereload(app);
80 }
81
82 if(_.isFunction(app.config.configureExpress)) {
83 app.config.configureExpress(app);
84 }
85
86 app.use('/*', function(req, res) {
87 res.render('index');
88 });
89
90 app.use(function(err, req, res, next) {
91 var resp = {
92 message: err.message
93 };
94 if(app.get('env') !== 'production') {
95 resp.error = err;
96 resp.stack = err.stack.split('\n');
97 }
98 res.json(resp);
99 });
100 };
101
102 app.start = function(config, done) {
103 if(done == null) {
104 done = function(err) {
105 if(err) {
106 throw err;
107 }
108
109 console.log("Application running on %s", app.config.host());
110 };
111 }
112
113 app.setup(config);
114 app.listen(app.config.port, app.config.ip, done);
115 };
116
117 return app;
118};
119
120exports.getDefaultConfig = function(root, env) {
121 return {
122 port: process.env.PORT || 3000,
123 ip: process.env.IP || '',
124
125 host: function() {
126 return exports.host(this.ip, this.port);
127 },
128
129 engine: 'jade',
130 views: path.resolve(root, 'views'),
131 public: path.resolve(root, 'public'),
132 favicon: path.resolve(root, 'public', 'favicon.ico'),
133
134 middlewareForStaticPublic: [],
135 configureAssetPipeline: function(assetPipeline) {},
136 configureExpress: function(app) {},
137
138 assets: {
139 mincer: Mincer,
140 root: root,
141 production: env === 'production',
142 mountPath: '/assets',
143 manifestFile: path.resolve(root, 'public', 'assets', 'manifest.json'),
144 paths: [],
145 scanDirectories: [
146 'assets'
147 ]
148 },
149
150 precompile: {
151 target: path.resolve(root, 'public', 'assets'),
152 images: true,
153 fonts: true,
154 files: [],
155 options: {}
156 },
157
158 livereload: {
159 active: env === 'development',
160 ip: process.env.LIVERELOAD_IP || 'localhost',
161 port: process.env.LIVERELOAD_PORT || 35729,
162 script: function () {
163 return util.format('//%s:%s/livereload.js', this.ip, this.port);
164 },
165 host: function() {
166 return exports.host(this.ip, this.port);
167 },
168 watch: [],
169 debug: env === 'development',
170 exts: [
171 'js',
172 'coffee',
173 'json',
174 'html',
175 'jade',
176 'ejs',
177 'css',
178 'styl',
179 'less',
180 'png',
181 'gif',
182 'jpg',
183 'svg',
184 'ico',
185 'eof',
186 'ttf',
187 'woff',
188 'woff2'
189 ]
190 }
191 };
192};
193
194/**
195 *
196 * @param {string} root
197 * @param {Array} directories
198 * @return {Array} asset paths
199 */
200exports.scanDirectories = function(root, directories) {
201 var results = [];
202 directories.forEach(function(directory) {
203 var directoryPath = path.resolve(root, directory);
204 if(fs.existsSync(directoryPath)) {
205 fs.readdirSync(directoryPath).forEach(function(file) {
206 var stat = fs.lstatSync(path.resolve(directoryPath, file));
207 if(stat.isDirectory()) {
208 results.push(path.join(directory, file));
209 }
210 });
211 } else {
212 debug('Directory "%s" does not exist', directory);
213 }
214 });
215
216 return results;
217};
218
219exports.livereload = function (app) {
220 if(app.config.livereload.active) {
221 debug('enable livereload server');
222 // Bind LiveReload Server
223 app.liveReloadServer = liveReload.createServer(_.merge({}, app.config.livereload));
224
225 // Override Debug Logger
226 // const _debug = app.liveReloadServer.debug;
227 app.liveReloadServer.debug = function (str) {
228 debug("LiveReload Server:", str);
229 };
230
231 // Load Watch Directories
232 var watch = app.config.livereload.watch;
233 exports.arrayPush(watch, app.get('views') + "/*");
234 exports.arrayPush(watch, app.config.assets.paths, function(p) {
235 return path.resolve(app.config.assets.root, p);
236 });
237
238 debug("LiveReload enabled.");
239 debug("Watching:", watch);
240 debug("LiveReload Server on", app.config.livereload.host());
241 // Start Server
242 app.liveReloadServer.watch(watch);
243
244 // Bind LRScript Middleware
245 app.use(function(req, res, next) {
246 res.locals.LRScriptFile = app.config.livereload.script();
247 res.locals.LRScriptTag = "<script src=\""+res.locals.LRScriptFile+"\"></'+'script>";
248 res.locals.LRScript = "<script>document.write('"+res.locals.LRScriptTag+"')</script>";
249 next();
250 });
251 } else {
252 debug('disable livereload server');
253 app.use(function (req, res, next) {
254 res.locals.LRScript = "<!-- LiveReload disabled -->";
255 next();
256 });
257 }
258};
259
260exports.arrayPush = function (target, item, transform) {
261 if (_.isUndefined(transform)) {
262 transform = function(x) { return x; };
263 }
264 item = _.isArray(item) ? item : [item];
265
266 item.forEach(function (p) {
267 target.push(transform(p));
268 });
269
270 return target;
271};
272
273exports.host = function(ip, port) {
274 if(_.isEmpty(ip) === false) {
275 return util.format('%s:%s', ip, port);
276 }
277
278 return util.format('localhost:%s', port);
279};
280
281exports.getPrecompileFonts = function() {
282 return [
283 '*.eot',
284 '*.svg',
285 '*.ttf',
286 '*.woff',
287 '*.woff2',
288 '**/*.eot',
289 '**/*.svg',
290 '**/*.ttf',
291 '**/*.woff',
292 '**/*.woff2'
293 ];
294};
295
296exports.getPrecompileImages = function() {
297 return [
298 '*.png',
299 '*.gif',
300 '*.jpg',
301 '*.ico',
302 '**/*.png',
303 '**/*.gif',
304 '**/*.jpg',
305 '**/*.ico'
306 ];
307};
308
309exports.pkg = pkg;