1 | var path = require('path');
|
2 | var fs = require('fs');
|
3 | var util = require('util');
|
4 |
|
5 | var pkg = require('../package.json');
|
6 |
|
7 | var debug = require('debug')('express-mincer-spa');
|
8 | debug.log = console.log.bind(console);
|
9 | var express = require('express');
|
10 | var _ = require('lodash');
|
11 | var favicon = require('serve-favicon');
|
12 | var ConnectMincer = require('connect-mincer');
|
13 | var Mincer = require('mincer');
|
14 | var liveReload = require('livereload');
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | exports = 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 |
|
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 |
|
65 | app.set('views', app.config.views);
|
66 | app.set('view engine', app.config.engine);
|
67 |
|
68 |
|
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 |
|
122 | exports.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 |
|
200 |
|
201 |
|
202 |
|
203 | exports.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 |
|
222 | exports.livereload = function (app) {
|
223 | if(app.config.livereload.active) {
|
224 | debug('enable livereload server');
|
225 |
|
226 | app.liveReloadServer = liveReload.createServer(_.merge({}, app.config.livereload));
|
227 |
|
228 |
|
229 |
|
230 | app.liveReloadServer.debug = function (str) {
|
231 | debug("LiveReload Server:", str);
|
232 | };
|
233 |
|
234 |
|
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 |
|
245 | app.liveReloadServer.watch(watch);
|
246 |
|
247 |
|
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 |
|
263 | exports.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 |
|
276 | exports.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 |
|
284 | exports.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 |
|
299 | exports.getPrecompileImages = function() {
|
300 | return [
|
301 | '*.png',
|
302 | '*.gif',
|
303 | '*.jpg',
|
304 | '*.ico',
|
305 | '**/*.png',
|
306 | '**/*.gif',
|
307 | '**/*.jpg',
|
308 | '**/*.ico'
|
309 | ];
|
310 | };
|
311 |
|
312 | exports.pkg = pkg;
|