1 | var ansi = require('ansi-escape-sequences')
|
2 | var pinoColada = require('pino-colada')
|
3 | var async = require('async-collection')
|
4 | var fsCompare = require('fs-compare')
|
5 | var pumpify = require('pumpify')
|
6 | var mkdirp = require('mkdirp')
|
7 | var path = require('path')
|
8 | var zlib = require('zlib')
|
9 | var pump = require('pump')
|
10 | var fs = require('fs')
|
11 |
|
12 | var bankai = require('../')
|
13 | var utils = require('./utils')
|
14 |
|
15 | module.exports = build
|
16 |
|
17 | function build (entry, outdir, opts) {
|
18 | var bankaiOpts = {
|
19 | logStream: pumpify(pinoColada(), process.stdout),
|
20 | watch: false,
|
21 | base: opts.base
|
22 | }
|
23 | var compiler = bankai(entry, bankaiOpts)
|
24 | var log = compiler.log
|
25 |
|
26 | if (!outdir) {
|
27 | outdir = path.join(compiler.dirname, 'dist')
|
28 | }
|
29 |
|
30 | mkdirp(outdir, function (err) {
|
31 | if (err) return console.error(err)
|
32 |
|
33 | log.info('Compiling & compressing files\n')
|
34 | created(compiler.dirname, outdir + '/')
|
35 |
|
36 | compiler.on('error', function (topic, sub, err) {
|
37 | if (err.pretty) log.error(err.pretty)
|
38 | else {
|
39 | log.error(`${topic}:${sub} ${err.message}\n${err.stack}`)
|
40 | }
|
41 | })
|
42 |
|
43 | compiler.on('ssr', function (result) {
|
44 | if (!result.success) log.warn('Server Side Rendering Skipped due to error: ' + result.error.message)
|
45 | })
|
46 |
|
47 | compiler.on('change', function (nodeName, edgeName, nodeState) {
|
48 | var stepName = nodeName + ':' + edgeName
|
49 | if (stepName === 'assets:list') writeAssets('assets')
|
50 | if (stepName === 'documents:list') writeDocuments('documents')
|
51 | if (stepName === 'scripts:list') writeScripts('scripts')
|
52 | if (stepName === 'service-worker:bundle') {
|
53 | var filename = compiler.graph.metadata.serviceWorker
|
54 | compiler.serviceWorker(writeSimple(filename, 'service-worker'))
|
55 | }
|
56 | })
|
57 |
|
58 | compiler.manifest(writeSimple('manifest.json', 'manifest'))
|
59 | compiler.styles('bundle.css', writeSingle('bundle.css', 'styles'))
|
60 | compiler.scripts('bundle.js', writeSingle('bundle.js', 'scripts'))
|
61 |
|
62 | function writeAssets () {
|
63 | var assets = compiler.graph.metadata.assets
|
64 | var list = Object.keys(assets)
|
65 | async.mapLimit(list, 3, copyFile, function (err) {
|
66 | if (err) return log.error(err)
|
67 | completed('assets')
|
68 | })
|
69 |
|
70 |
|
71 |
|
72 | function copyFile (src, done) {
|
73 | var dest = path.join(outdir, path.relative(compiler.dirname, src))
|
74 | var dirname = utils.dirname(dest)
|
75 |
|
76 | if (dirname === dest) {
|
77 | copy(done)
|
78 | } else {
|
79 | mkdirp(dirname, function (err) {
|
80 | if (err) return done(err)
|
81 | copy(done)
|
82 | })
|
83 | }
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | function copy (done) {
|
89 | fsCompare.mtime(src, dest, function (err, diff) {
|
90 | if (err) return done(err)
|
91 | if (diff === 1) {
|
92 | if (fs.copyFile) fs.copyFile(src, dest, fin)
|
93 | else pump(fs.createReadStream(src), fs.createWriteStream(dest), fin)
|
94 | }
|
95 | })
|
96 | function fin (err) {
|
97 | if (err) return done(err)
|
98 | created(compiler.dirname, dest)
|
99 | done()
|
100 | }
|
101 | }
|
102 | }
|
103 | }
|
104 |
|
105 | function writeScripts (step) {
|
106 | var node = compiler.graph.data[step].list
|
107 | var list = String(node.buffer)
|
108 | list = list.length ? list.split(',') : []
|
109 | async.mapLimit(list, 3, iterator, function (err) {
|
110 | if (err) return log.error(err)
|
111 | completed(step)
|
112 | })
|
113 |
|
114 | function iterator (filename, done) {
|
115 | compiler[step](filename, writeSingle(filename, step))
|
116 | }
|
117 | }
|
118 |
|
119 | function writeDocuments (step) {
|
120 | var write = writeCompressed
|
121 |
|
122 | var node = compiler.graph.data[step].list
|
123 | var list = String(node.buffer).split(',')
|
124 | async.mapLimit(list, 3, iterator, function (err) {
|
125 | if (err) return log.error(err)
|
126 | completed(step)
|
127 | })
|
128 |
|
129 | function iterator (filename, done) {
|
130 |
|
131 |
|
132 |
|
133 | if (/[:*]/.test(filename)) return done()
|
134 | compiler[step](filename, function (err, node) {
|
135 | if (err) return done(err)
|
136 | filename = path.join(outdir, filename, 'index.html')
|
137 | var dirname = utils.dirname(filename)
|
138 | if (dirname === filename) {
|
139 | write(filename, node.buffer, done)
|
140 | } else {
|
141 | mkdirp(dirname, function (err) {
|
142 | if (err) return done(err)
|
143 | write(filename, node.buffer, done)
|
144 | })
|
145 | }
|
146 | })
|
147 | }
|
148 | }
|
149 |
|
150 | function writeSimple (filename, type) {
|
151 | return function (err, node) {
|
152 | if (err) return log.error(err)
|
153 | filename = path.join(outdir, filename)
|
154 | writeCompressed(filename, node.buffer, function (err) {
|
155 | if (err) return log.error(err)
|
156 | completed(type)
|
157 | })
|
158 | }
|
159 | }
|
160 |
|
161 | function writeSingle (filename, type) {
|
162 | return function (err, node) {
|
163 | if (err) return log.error(err)
|
164 | var dirname = path.join(outdir, node.hash.toString('hex').slice(0, 16))
|
165 | mkdirp(dirname, function (err) {
|
166 | if (err) return log.error(err)
|
167 | var sourcemapNode = type === 'scripts' && compiler.graph.data[type][`${filename}.map`]
|
168 | var sourcemap = path.join(dirname, `${filename}.map`)
|
169 | filename = path.join(dirname, filename)
|
170 | writeCompressed(filename, node.buffer, function (err) {
|
171 | if (err) return log.error(err)
|
172 | if (!sourcemapNode) {
|
173 | return completed(type)
|
174 | }
|
175 | writeCompressed(sourcemap, sourcemapNode.buffer, function (err) {
|
176 | if (err) return log.error(err)
|
177 | completed(type)
|
178 | })
|
179 | })
|
180 | })
|
181 | }
|
182 | }
|
183 |
|
184 | function created (basedir, filename) {
|
185 | var relative = path.relative(process.cwd(), filename)
|
186 | log.info(`${clr('created', 'magenta')}: ${relative}`)
|
187 | }
|
188 |
|
189 | function completed (step) {
|
190 | log.debug(`${clr('completed', 'green')}: ${step}`)
|
191 | }
|
192 |
|
193 | function writeCompressed (filename, buffer, done) {
|
194 | async.series([
|
195 | function raw (done) {
|
196 | fs.writeFile(filename, buffer, function (err) {
|
197 | if (err) return done(err)
|
198 | created(compiler.dirname, filename)
|
199 | done()
|
200 | })
|
201 | },
|
202 | function gzip (done) {
|
203 | zlib.gzip(buffer, function (err, compressed) {
|
204 | if (err) return done(err)
|
205 | var outfile = filename + '.gz'
|
206 | fs.writeFile(outfile, compressed, function (err) {
|
207 | if (err) return done(err)
|
208 | created(compiler.dirname, outfile)
|
209 | done()
|
210 | })
|
211 | })
|
212 | },
|
213 | function deflate (done) {
|
214 | zlib.deflate(buffer, function (err, compressed) {
|
215 | if (err) return done(err)
|
216 | var outfile = filename + '.deflate'
|
217 | fs.writeFile(outfile, compressed, function (err) {
|
218 | if (err) return done(err)
|
219 | created(compiler.dirname, outfile)
|
220 | done()
|
221 | })
|
222 | })
|
223 | },
|
224 | function compressBrotli (done) {
|
225 | utils.brotli(buffer).then(function (compressed) {
|
226 | var outfile = filename + '.br'
|
227 | fs.writeFile(outfile, compressed, function (err) {
|
228 | if (err) return done(err)
|
229 | created(compiler.dirname, outfile)
|
230 | done()
|
231 | })
|
232 | }).catch(done)
|
233 | }
|
234 | ], done)
|
235 | }
|
236 | })
|
237 | }
|
238 |
|
239 | function clr (text, color) {
|
240 | return process.stdout.isTTY ? ansi.format(text, color) : text
|
241 | }
|