-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
162 lines (142 loc) · 6.03 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* fb-flo-brunch -- A seamless plugin for using fb-flo on top of Brunch.
*
* http://brunch.io
* https://facebook.github.io/fb-flo/
*/
"use strict";
var anymatch = require('anymatch');
var defaults = require('amp-defaults');
var extend = require('amp-extend');
var flo = require('fb-flo');
var fs = require('fs');
var path = require('path');
var pick = require('amp-pick');
// Plugin constructor. Takes Brunch's complete config as argument.
function FbFloBrunch(config) {
this.setOptions(config);
// This plugin only makes sense if we're in watcher mode
// (`config.persistent`). Also, we honor the plugin-specific `enabled`
// setting, so users don't need to strip the plugin or its config to
// disable it: just set `enabled` to `false`.
if (!config || !config.persistent || false === this.config.enabled) {
return;
}
this.resolver = this.resolver.bind(this);
this.startServer();
}
// These options are passed untouched to fb-flo. See
// https://github.com/facebook/fb-flo#1-configure-fb-flo-server for details.
var FB_FLO_OPTIONS = FbFloBrunch.FB_FLO_OPTIONS = Object.freeze([
'host', 'pollingInterval', 'port',
'useFilePolling', 'useWatchman', 'verbose', 'watchDotFiles']);
// This is a superset of options, including all plugin-specific options.
// See https://deliciousinsights.github.io/fb-flo-brunch for details.
var OPTIONS = FbFloBrunch.OPTIONS = Object.freeze(FB_FLO_OPTIONS.concat(
['enabled', 'message', 'messageColor', 'messageLevel',
'messageResourceColor', 'resolverMatch', 'resolverReload']));
// Default values for options.
FbFloBrunch.DEFAULTS = {
// The message logged in the browser console when the fb-flo extension
// has live-injected the resource. `%s` is replaced by the resource name.
message: '%s has just been updated with new content',
// The console level at which to output the message. Can be any valid
// console method, but will usually be one of `log`, `info`, or `debug`.
messageLevel: 'log'
};
extend(FbFloBrunch.prototype, {
// This is the marker that makes Brunch acknowledge this as a plugin.
brunchPlugin: true,
// The fb-flo resolver, triggered everytime fb-flo wants to send a change
// notification to connected browser extensions.
resolver: function resolver(filePath, callback) {
var fullPath = path.join(this.config.publicPath, filePath);
var options = {
resourceURL: filePath,
contents: fs.readFileSync(fullPath).toString()
};
// If we defined a custom browser-side message system, use it.
if (this.update) {
options.update = this.update;
}
// If we specified a `match` option for fb-flo notifications, use it.
if (this.config.resolverMatch) {
options.match = this.config.resolverMatch;
}
// If we defined an advanced reload mechanism (fb-flo only has true/false,
// we also allow anymatch sets), use it.
if (this.config.resolverReload) {
options.reload = this.config.resolverReload(filePath);
}
// Hand the notification over to the fb-flo server.
callback(options);
},
// Configuration interpretation logic: defaults, method generation, etc.
setOptions: function setOptions(config) {
// We expect config at `config.plugins.fbFlo`.
var cfg = config && config.plugins && config.plugins.fbFlo || {};
this.config = defaults(pick(cfg, OPTIONS), FbFloBrunch.DEFAULTS);
// We're using the configuration's public path, or its Brunch default,
// to know what directory fb-flo should watch.
this.config.publicPath = config && config.paths && config.paths.public ||
path.join(process.cwd(), 'public');
// Allow for basic, fb-flo supported true/false values for the resolver's
// `reload` setting, but also allow anymatch sets (way more flexible).
if (this.config.resolverReload) {
if (true === this.config.resolverReload) {
this.config.resolverReload = function alwaysReload() { return true; };
} else {
this.config.resolverReload = anymatch(this.config.resolverReload);
}
}
// If a client-side message is defined, build the `update` resolver method.
if (this.config.message) {
var msg = this.config.message.toString().replace(/"/g, '\\"');
var suffix = ['_resourceURL'];
// We allow custom colors for the resource name and overall text
// (this currently only works in Chrome DevTools; but so does fb-flo…).
// Any valid CSS color syntax is usable.
if (this.config.messageResourceColor) {
msg = msg.replace('%s', '%c%s%c');
suffix.unshift('"color: ' + this.config.messageResourceColor + '"');
suffix.push('"color: ' + (this.config.messageColor || 'black') + '"');
}
if (this.config.messageColor) {
msg = '%c' + msg;
suffix.unshift('"color: ' + this.config.messageColor + '"');
}
suffix = suffix.join(', ');
var level = this.config.messageLevel;
var code = 'console.' + level + '("' + msg + '", ' + suffix + ');';
// Closures won't marshal over to the browser, hence the autonomous
// function code generation.
/* jshint evil:true */
this.update = new Function('_window', '_resourceURL', code);
}
},
// Starts the fb-flo server. Launched from the constructor unless the
// plugin is disabled.
startServer: function startServer() {
this._flo = flo(
// The path to watch (see `setOptions(…)`).
this.config.publicPath,
// The config-provided fb-flo options + a generic watch glob
// (all JS/CSS, at any depth) inside the watched path.
extend(
pick(this.config, FB_FLO_OPTIONS),
{ glob: ['**/*.js', '**/*.css'] }
),
// Our resolver method (it was properly bound upon construction).
this.resolver
);
},
// Stops the fb-flo server when Brunch shuts down.
teardown: function tearDownFbFlo() {
// If the plugin was disabled, we didn't start the server, so check.
if (this._flo) {
this._flo.close();
}
}
});
// A Brunch plugin's default export must be the constructor.
module.exports = FbFloBrunch;