UNPKG

7.23 kBJavaScriptView Raw
1var Emitter = require('events').EventEmitter
2var debug = require('debug')('bankai')
3var graph = require('buffer-graph')
4var assert = require('assert')
5var path = require('path')
6var pino = require('pino')
7
8var localization = require('./localization')
9var queue = require('./lib/queue')
10var utils = require('./lib/utils')
11
12var assetsNode = require('./lib/graph-assets')
13var documentNode = require('./lib/graph-document')
14var manifestNode = require('./lib/graph-manifest')
15var reloadNode = require('./lib/graph-reload')
16var scriptNode = require('./lib/graph-script')
17var serviceWorkerNode = require('./lib/graph-service-worker')
18var styleNode = require('./lib/graph-style')
19
20module.exports = Bankai
21
22function Bankai (entry, opts) {
23 if (!(this instanceof Bankai)) return new Bankai(entry, opts)
24 opts = opts || {}
25 this.local = localization(opts.language || 'en-US')
26 this.log = pino(opts.logStream || process.stdout)
27
28 assert.equal(typeof entry, 'string', 'bankai: entry should be type string')
29 assert.ok(path.isAbsolute(entry), 'bankai: entry should be an absolute path. Received: ' + entry)
30 assert.equal(typeof opts, 'object', 'bankai: opts should be type object')
31
32 var self = this
33 var methods = [
34 'manifest',
35 'assets',
36 'service-worker',
37 'scripts',
38 'styles',
39 'documents'
40 ]
41
42 // Initialize data structures.
43 var key = Buffer.from('be intolerant of intolerance')
44 this.dirname = utils.dirname(entry) // The base directory.
45 this.queue = queue(methods) // The queue caches requests until ready.
46 this.graph = graph(key) // The graph manages relations between deps.
47
48 // Detect when we're ready to allow requests to go through.
49 this.graph.on('change', function (nodeName, edgeName, state) {
50 self.emit('change', nodeName, edgeName, state)
51 var eventName = nodeName + ':' + edgeName
52 var count = self.metadata.count
53 var queue = self.queue
54
55 if (eventName === 'assets:list') {
56 count['assets'] = String(self.graph.data.assets.list.buffer).split(',').length
57 queue['assets'].ready()
58 } else if (eventName === 'documents:list') {
59 count['documents'] = String(self.graph.data.documents.list.buffer).split(',').length
60 queue['documents'].ready()
61 } else if (eventName === 'manifest:bundle') {
62 count['manifest'] = 1
63 queue['manifest'].ready()
64 } else if (eventName === 'scripts:bundle') {
65 count['scripts'] = 1
66 queue['scripts'].ready()
67 } else if (eventName === 'service-worker:bundle') {
68 count['service-worker'] = 1
69 queue['service-worker'].ready()
70 } else if (eventName === 'styles:bundle') {
71 count['styles'] = 1
72 queue['styles'].ready()
73 }
74 })
75
76 // Handle errors so they can be logged.
77 this.graph.on('error', function () {
78 var args = ['error']
79 for (var len = arguments.length, i = 0; i < len; i++) {
80 args.push(arguments[i])
81 }
82 self.emit.apply(self, args)
83 })
84
85 this.graph.on('progress', function (chunk, value) {
86 self.emit('progress', chunk, value)
87 })
88
89 this.graph.on('ssr', function (result) {
90 self.emit('ssr', result)
91 })
92
93 // Insert nodes into the graph.
94 this.graph.node('assets', assetsNode)
95 this.graph.node('documents', [ 'assets:list', 'manifest:bundle', 'styles:bundle', 'scripts:bundle', 'reload:bundle' ], documentNode)
96 this.graph.node('manifest', manifestNode)
97 this.graph.node('scripts', scriptNode)
98 this.graph.node('reload', reloadNode)
99 this.graph.node('service-worker', [ 'assets:list', 'styles:bundle', 'scripts:bundle', 'documents:list' ], serviceWorkerNode)
100 this.graph.node('styles', [ 'scripts:style', 'scripts:bundle' ], styleNode)
101
102 // Kick off the graph.
103 this.graph.start({
104 dirname: this.dirname,
105 watch: opts.watch !== false,
106 fullPaths: opts.fullPaths,
107 reload: Boolean(opts.reload),
108 log: this.log,
109 watchers: {},
110 entry: entry,
111 opts: opts,
112 count: {
113 assets: 0,
114 documents: 0,
115 manifest: 0,
116 scripts: 0,
117 'service-worker': 0,
118 style: 0
119 }
120 })
121
122 this.metadata = this.graph.metadata
123}
124Bankai.prototype = Object.create(Emitter.prototype)
125
126Bankai.prototype.scripts = function (filename, cb) {
127 assert.equal(typeof filename, 'string')
128 assert.equal(typeof cb, 'function')
129 var stepName = 'scripts'
130 var edgeName = filename.split('.')[0]
131 var self = this
132 this.queue[stepName].add(function () {
133 var data = self.graph.data[stepName][edgeName]
134 if (!data) return cb(new Error(`bankai.scripts: could not find a bundle for "${filename}"`))
135 cb(null, data)
136 })
137}
138
139Bankai.prototype.styles = function (filename, cb) {
140 assert.equal(typeof filename, 'string')
141 assert.equal(typeof cb, 'function')
142 var stepName = 'styles'
143 var edgeName = filename.split('.')[0]
144 var self = this
145 this.queue[stepName].add(function () {
146 var data = self.graph.data[stepName][edgeName]
147 if (!data) return cb(new Error('bankai.styles: could not find bundle'))
148 cb(null, data)
149 })
150}
151
152Bankai.prototype.documents = function (url, cb) {
153 assert.equal(typeof url, 'string')
154 assert.equal(typeof cb, 'function')
155
156 var filename = url.split('?')[0]
157
158 if (filename === '/') filename = 'index'
159 var stepName = 'documents'
160 var edgeName = filename.split('.')[0] + '.html'
161 var self = this
162 this.queue[stepName].add(function () {
163 var data = self.graph.data[stepName][edgeName]
164 if (!data) return cb(new Error('bankai.document: could not find a document for ' + filename))
165 cb(null, data)
166 })
167}
168
169Bankai.prototype.manifest = function (cb) {
170 assert.equal(typeof cb, 'function')
171 var stepName = 'manifest'
172 var edgeName = 'bundle'
173 var self = this
174 this.queue[stepName].add(function () {
175 var data = self.graph.data[stepName][edgeName]
176 if (!data) return cb(new Error('bankai.manifest: could not find bundle'))
177 cb(null, data)
178 })
179}
180
181Bankai.prototype.serviceWorker = function (cb) {
182 assert.equal(typeof cb, 'function')
183 var stepName = 'service-worker'
184 var edgeName = 'bundle'
185 var self = this
186 this.queue[stepName].add(function () {
187 var data = self.graph.data[stepName][edgeName]
188 if (!data) return cb(new Error('bankai.serviceWorker: could not find bundle'))
189 cb(null, data)
190 })
191}
192
193Bankai.prototype.assets = function (filename, cb) {
194 assert.equal(typeof filename, 'string')
195 assert.equal(typeof cb, 'function')
196 var stepName = 'assets'
197 var self = this
198 this.queue[stepName].add(function () {
199 filename = path.join(self.dirname, filename)
200 var data = self.metadata.assets[filename]
201 if (!data) return cb(new Error('bankai.asset: could not find a file for ' + filename))
202 cb(null, filename)
203 })
204}
205
206Bankai.prototype.sourceMaps = function (stepName, edgeName, cb) {
207 assert.equal(typeof stepName, 'string')
208 assert.equal(typeof edgeName, 'string')
209 assert.equal(typeof cb, 'function')
210 edgeName = /\.map$/.test(edgeName) ? edgeName : edgeName + '.map'
211 var self = this
212 var data = self.graph.data[stepName][edgeName]
213 if (!data) return cb(new Error('bankai.sourceMaps: could not find a file for ' + stepName + ':' + edgeName))
214 cb(null, data)
215}
216
217Bankai.prototype.close = function () {
218 debug('closing all file watchers')
219 this.graph.emit('close')
220 this.emit('close')
221}