UNPKG

7.33 kBJavaScriptView Raw
1/*
2 * pub-server serve-scripts.js
3 *
4 * serves browserified scripts
5 * as well as /pub/* routes for opts, plugins and source files.
6
7 * API: serveStatics(opts, server) returns serveStatics object
8 * server optional, if not passed, no routes served
9 * serveStatics.outputAll() - copy scripts to outputs[0] (for pub -O)
10 *
11 * copyright 2015, Jurgen Leschner - github.com/jldec - MIT license
12 */
13
14var debug = require('debug')('pub:scripts');
15var u = require('pub-util');
16var through = require('through2');
17var fspath = require('path');
18var fs = require('fs-extra');
19var babelify = require('babelify');
20
21module.exports = function serveScripts(opts, server) {
22
23 if (!(this instanceof serveScripts)) return new serveScripts(opts);
24 var self = this;
25 var log = opts.log;
26
27 self.serveRoutes = serveRoutes;
28 self.outputAll = outputAll; // for pub -O
29
30 var browserify = require('browserify-middleware');
31
32 // expose build-bundle for output to file
33 browserify.buildBundle = require('browserify-middleware/lib/build-bundle.js');
34
35 /* browsrify pregen with production is slow */
36 if ((opts.outputOnly || opts.minify) && !opts.dbg) { browserify.settings.mode = 'production'; }
37
38 browserify.settings( { ignore: ['request', 'request-debug', 'graceful-fs', 'resolve', 'osenv', 'tmp'],
39 ignoreMissing: false } );
40
41 browserify.settings.production('cache', '1h');
42
43 // prepare array of browserscripts including builtins
44 self.scripts = u.map(opts.browserScripts, function(script) {
45 var o = {
46 route: script.route,
47 path: script.path,
48 delay: script.delay,
49 opts: u.omit(script, 'path', 'route', 'inject', 'maxAge')
50 }
51 if ('maxAge' in script) { o.opts.cache = script.maxAge || 'dynamic'; }
52 return o;
53 });
54
55 self.scripts.push( {
56 route: '/server/pub-sockets.js',
57 path: fspath.join(__dirname, '../client/pub-sockets.js')
58 } );
59
60 // editor scripts
61 if (opts.editor) {
62
63 if (!opts.spa) {
64 self.scripts.push( {
65 route: '/pub/pub-ux.js',
66 path: fspath.join(__dirname, '../client/pub-ux.js')
67 } );
68 }
69
70 self.scripts.push( {
71 route: '/pub/_generator.js',
72 path: fspath.join(__dirname, '../client/_generator.js'),
73 } );
74
75 self.scripts.push( {
76 route: '/pub/_generator-plugins.js',
77 path: fspath.join(__dirname, '../client/_generator-plugins.js'),
78 opts: { transform: [transformPlugins] }
79 } );
80 }
81
82 return;
83
84 //--//--//--//--//--//--//--//--//--//--//--//--//--//--//--//
85
86 // deploy browserify scripts and editor/admin handlers
87 function serveRoutes(server) {
88 var app = server.app;
89 var generator = server.generator;
90
91 // route browserscripts, including builtins
92 u.each(self.scripts, function(script) {
93 var handler = browserify(script.path, script.opts);
94 if (script.delay) {
95 var delayed = function(req, res) {
96 debug(req.path, 'waiting', script.delay);
97 setTimeout(function() {
98 debug(req.path, 'done waiting', script.delay);
99 handler(req, res);
100 }, u.ms(script.delay));
101 }
102 }
103 app.get(script.route, delayed || handler);
104 });
105
106 // editor api
107 if (opts.editor) {
108 app.post('/pub/_files', function(req, res) {
109 generator.serverSave(req.body, req.user, function(err, results) {
110 if (err) return res.status(500).send(err);
111 res.status(200).send(results);
112 })
113 });
114 app.get('/pub/_opts.json', function(req, res) {
115 res.set('Cache-Control', 'no-cache');
116 res.send(serializeOpts(server.generator));
117 });
118 }
119
120 // admin api
121 app.get('/admin/flushCaches', function(req, res) {
122 generator.flushCaches(function(err, results) {
123 if (err) return res.status(500).send(err);
124 res.status(200).send(results);
125 })
126 });
127
128 app.get('/admin/reloadSources', function(req, res) {
129 res.send(generator.reloadSources(req.query.src));
130 });
131
132 app.get('/admin/outputPages', function(req, res) {
133 res.send(generator.outputPages(req.query.output));
134 });
135
136 app.get('/admin/logPages', function(req, res) {
137 res.send(generator.logPages());
138 });
139
140 app.get('/admin/reload', function(req, res) {
141 generator.reload()
142 res.status(200).send('OK');
143 });
144 }
145
146 // publish browserscripts
147 function outputAll(generator) {
148
149 var output = (opts.outputs && opts.outputs[0]);
150 if (!output) return log('scripts.outputAll: no output');
151
152 var omit = output.omitRoutes;
153 if (omit && !u.isArray(omit)) { omit = [omit]; }
154
155 // TODO: re-use similar filter in server/serve-statics and generator.output
156 var filterRe = new RegExp( '^(/admin/|/server/' +
157 (opts.editor ? '' : '|/pub/') +
158 (omit ? '|' + u.map(omit, u.escapeRegExp).join('|') : '') +
159 ')');
160
161 u.each(self.scripts, function(script) {
162 if (filterRe.test(script.route)) return;
163
164 var out = fspath.join(output.path, script.route);
165 var ws = fs.createOutputStream(out);
166 ws.on('finish', function() {
167 log('output script: %s', out);
168 });
169 ws.on('error', log);
170
171 // from browserify-middleware index.js (may need to do noParse map also)
172 var options = browserify.settings.normalize(script.opts);
173 var bundler = browserify.buildBundle(script.path, options);
174 if (!opts.dbg) { bundler.plugin(require.resolve('minifyify'), { map:false } ); }
175 bundler.bundle().pipe(ws);
176 });
177
178 if (opts.editor) {
179 var out = fspath.join(output.path, '/pub/_opts.json');
180 fs.outputJson(out, serializeOpts(generator, true, output), function(err) {
181 log(err || 'output opts: %s', out);
182 });
183 }
184 }
185
186 // browserify transform for sending plugins
187 // invoked using require('./__plugins')
188 function transformPlugins(path) {
189 if (!/_generator-plugins/.test(path)) return through();
190 return through(
191 function tform(chunk, enc, cb) { cb() }, // ignore input
192 function flush(cb) {
193 this.push(requirePlugins());
194 cb();
195 }
196 );
197 }
198
199 function requirePlugins() {
200 var s = u.reduce(opts.generatorPlugins.reverse(),
201 function(memo, plugin) {
202 return memo + 'require("' + plugin.path + '")(generator);\n';
203 }, '');
204
205 return s;
206 }
207
208 function serializeOpts(generator, toStatic, outputDest) {
209 var sOpts = u.omit(opts, 'output$', 'source$', 'log', 'session');
210
211 // provide for detection of static hosted editor
212 if (toStatic) { sOpts.staticHost = true; }
213
214 // pass output.fqImages -> static opts for use in static editor/generator
215 if (outputDest && outputDest.fqImages) { sOpts.fqImages = outputDest.fqImages; }
216
217 sOpts.staticPaths = u.map(opts.staticPaths, function(staticPath) {
218 return u.omit(staticPath, 'files', 'src');
219 });
220 sOpts.outputs = u.map(opts.outputs, function(output) {
221 return u.omit(output, 'files', 'src');
222 });
223 sOpts.sources = u.map(opts.sources, function(source) {
224 var rawSource = u.omit(source, 'files', 'src', 'file$', 'fragments', 'updates', 'snapshots', 'drafts', 'cache');
225 rawSource.files = source.type === 'FILE' ?
226 generator.serializeFiles(source.files) :
227 source.files;
228 return rawSource;
229 });
230 return sOpts;
231 }
232
233}