UNPKG

6.62 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-ux.js',
57 path: fspath.join(__dirname, '../client/pub-ux.js')
58 } );
59
60 // editor scripts
61 if (opts.editor) {
62
63 self.scripts.push( {
64 route: '/pub/_generator.js',
65 path: fspath.join(__dirname, '../client/_generator.js'),
66 } );
67
68 self.scripts.push( {
69 route: '/pub/_generator-plugins.js',
70 path: fspath.join(__dirname, '../client/_generator-plugins.js'),
71 opts: { transform: [transformPlugins] }
72 } );
73 }
74
75 return;
76
77 //--//--//--//--//--//--//--//--//--//--//--//--//--//--//--//
78
79 // deploy browserify scripts and editor/admin handlers
80 function serveRoutes(server) {
81 var app = server.app;
82 var generator = server.generator;
83
84 // route browserscripts, including builtins
85 u.each(self.scripts, function(script) {
86 var handler = browserify(script.path, script.opts);
87 if (script.delay) {
88 var delayed = function(req, res) {
89 debug(req.path, 'waiting', script.delay);
90 setTimeout(function() {
91 debug(req.path, 'done waiting', script.delay);
92 handler(req, res);
93 }, u.ms(script.delay));
94 }
95 }
96 app.get(script.route, delayed || handler);
97 });
98
99 // editor api
100 if (opts.editor) {
101 app.post('/pub/_files', function(req, res) {
102 generator.serverSave(req.body, req.user, function(err, results) {
103 if (err) return res.status(500).send(err);
104 res.status(200).send(results);
105 })
106 });
107 app.get('/pub/_opts.json', function(req, res) {
108 res.set('Cache-Control', 'no-cache');
109 res.send(serializeOpts(server.generator));
110 });
111 }
112
113 // admin api
114 app.get('/admin/flushCaches', function(req, res) {
115 generator.flushCaches(function(err, results) {
116 if (err) return res.status(500).send(err);
117 res.status(200).send(results);
118 })
119 });
120
121 app.get('/admin/reloadSources', function(req, res) {
122 res.send(generator.reloadSources(req.query.src));
123 });
124
125 app.get('/admin/outputPages', function(req, res) {
126 res.send(generator.outputPages(req.query.output));
127 });
128
129 app.get('/admin/reload', function(req, res) {
130 generator.reload()
131 res.status(200).send('OK');
132 });
133 }
134
135 // publish browserscripts
136 function outputAll(generator) {
137
138 var dest = (opts.outputs && opts.outputs[0]);
139 if (!dest) return log('scripts.outputAll: no output');
140
141 u.each(self.scripts, function(script) {
142 var out = fspath.join(dest.path, script.route);
143 var ws = fs.createOutputStream(out);
144 ws.on('finish', function() {
145 log('output script: %s', out);
146 });
147 ws.on('error', log);
148
149 // from browserify-middleware index.js (may need to do noParse map also)
150 var options = browserify.settings.normalize(script.opts);
151 var bundler = browserify.buildBundle(script.path, options);
152 if (!opts.dbg) { bundler.plugin(require.resolve('minifyify'), { map:false } ); }
153 bundler.bundle().pipe(ws);
154 });
155
156 if (opts.editor) {
157 var out = fspath.join(dest.path, '/pub/_opts.json');
158 fs.outputJson(out, serializeOpts(generator, true, dest), function(err) {
159 log(err || 'output opts: %s', out);
160 });
161 }
162 }
163
164 // browserify transform for sending plugins
165 // invoked using require('./__plugins')
166 function transformPlugins(path) {
167 if (!/_generator-plugins/.test(path)) return through();
168 return through(
169 function tform(chunk, enc, cb) { cb() }, // ignore input
170 function flush(cb) {
171 this.push(requirePlugins());
172 cb();
173 }
174 );
175 }
176
177 function requirePlugins() {
178 var s = u.reduce(opts.generatorPlugins.reverse(),
179 function(memo, plugin) {
180 return memo + 'require("' + plugin.path + '")(generator);\n';
181 }, '');
182
183 return s;
184 }
185
186 function serializeOpts(generator, toStatic, outputDest) {
187 var sOpts = u.omit(opts, 'output$', 'source$', 'log', 'session');
188
189 // provide for detection of static hosted editor
190 if (toStatic) { sOpts.staticHost = true; }
191
192 // pass output.fqImages -> static opts for use in static editor/generator
193 if (outputDest && outputDest.fqImages) { sOpts.fqImages = outputDest.fqImages; }
194
195 sOpts.staticPaths = u.map(opts.staticPaths, function(staticPath) {
196 return u.omit(staticPath, 'files', 'src');
197 });
198 sOpts.outputs = u.map(opts.outputs, function(output) {
199 return u.omit(output, 'files', 'src');
200 });
201 sOpts.sources = u.map(opts.sources, function(source) {
202 var rawSource = u.omit(source, 'files', 'src', 'file$', 'fragments', 'updates', 'snapshots', 'drafts', 'cache');
203 rawSource.files = source.type === 'FILE' ?
204 generator.serializeFiles(source.files) :
205 source.files;
206 return rawSource;
207 });
208 return sOpts;
209 }
210
211}