1
1
/* @flow */
2
+ import readline from 'readline' ;
3
+ import tty from 'tty' ;
4
+
2
5
import type FirefoxProfile from 'firefox-profile' ;
3
6
import type Watchpack from 'watchpack' ;
4
7
@@ -41,18 +44,17 @@ export type WatcherCreatorParams = {|
41
44
sourceDir : string ,
42
45
artifactsDir : string ,
43
46
onSourceChange ?: OnSourceChangeFn ,
44
- desktopNotifications ?: typeof defaultDesktopNotifications ,
45
47
ignoreFiles ?: Array < string > ,
46
48
createFileFilter ?: FileFilterCreatorFn ,
49
+ addonReload : typeof defaultAddonReload ,
47
50
| } ;
48
51
49
52
export type WatcherCreatorFn = ( params : WatcherCreatorParams ) => Watchpack ;
50
53
51
54
export function defaultWatcherCreator (
52
55
{
53
- addonId, client, sourceDir, artifactsDir, ignoreFiles,
56
+ addonId, addonReload , client, sourceDir, artifactsDir, ignoreFiles,
54
57
onSourceChange = defaultSourceWatcher ,
55
- desktopNotifications = defaultDesktopNotifications ,
56
58
createFileFilter = defaultFileFilterCreator ,
57
59
} : WatcherCreatorParams
58
60
) : Watchpack {
@@ -62,24 +64,37 @@ export function defaultWatcherCreator(
62
64
return onSourceChange ( {
63
65
sourceDir,
64
66
artifactsDir,
65
- onChange : ( ) => {
66
- log . debug ( `Reloading add-on ID ${ addonId } ` ) ;
67
-
68
- return client . reloadAddon ( addonId )
69
- . catch ( ( error ) => {
70
- log . error ( '\n' ) ;
71
- log . error ( error . stack ) ;
72
- desktopNotifications ( {
73
- title : 'web-ext run: error occurred' ,
74
- message : error . message ,
75
- } ) ;
76
- throw error ;
77
- } ) ;
78
- } ,
67
+ onChange : ( ) => addonReload ( { addonId, client} ) ,
79
68
shouldWatchFile : ( file ) => fileFilter . wantFile ( file ) ,
80
69
} ) ;
81
70
}
82
71
72
+ export type ReloadParams = { |
73
+ addonId : string ,
74
+ client : RemoteFirefox ,
75
+ desktopNotifications ?: typeof defaultDesktopNotifications ,
76
+ | } ;
77
+
78
+ export async function defaultAddonReload (
79
+ {
80
+ addonId, client,
81
+ desktopNotifications = defaultDesktopNotifications ,
82
+ } : ReloadParams
83
+ ) : Promise < void > {
84
+ log . debug ( `Reloading add-on ID ${ addonId } ` ) ;
85
+ try {
86
+ await client . reloadAddon ( addonId ) ;
87
+ } catch ( reloadError ) {
88
+ log . error ( '\n' ) ;
89
+ log . error ( reloadError . stack ) ;
90
+ desktopNotifications ( {
91
+ title : 'web-ext run: error occurred' ,
92
+ message : reloadError . message ,
93
+ } ) ;
94
+ throw reloadError ;
95
+ }
96
+ }
97
+
83
98
84
99
// defaultReloadStrategy types and implementation.
85
100
@@ -94,8 +109,10 @@ export type ReloadStrategyParams = {|
94
109
| } ;
95
110
96
111
export type ReloadStrategyOptions = { |
112
+ addonReload ? : typeof defaultAddonReload ,
97
113
createWatcher ? : WatcherCreatorFn ,
98
114
createFileFilter ? : FileFilterCreatorFn ,
115
+ stdin : stream$Readable ,
99
116
| } ;
100
117
101
118
export function defaultReloadStrategy (
@@ -104,18 +121,50 @@ export function defaultReloadStrategy(
104
121
sourceDir, artifactsDir, ignoreFiles,
105
122
} : ReloadStrategyParams ,
106
123
{
124
+ addonReload = defaultAddonReload ,
107
125
createWatcher = defaultWatcherCreator ,
126
+ stdin = process . stdin ,
108
127
} : ReloadStrategyOptions = { }
109
128
) : void {
110
- const watcher : Watchpack = (
111
- createWatcher ( { addonId, client, sourceDir, artifactsDir, ignoreFiles} )
112
- ) ;
129
+ const watcher : Watchpack = createWatcher ( {
130
+ addonId, addonReload , client, sourceDir, artifactsDir, ignoreFiles,
131
+ } ) ;
113
132
114
133
firefoxProcess . on ( 'close' , ( ) => {
115
134
client . disconnect ( ) ;
116
135
watcher . close ( ) ;
136
+ stdin . pause ( ) ;
117
137
} ) ;
118
138
139
+ if ( stdin . isTTY && stdin instanceof tty . ReadStream ) {
140
+ readline . emitKeypressEvents ( stdin ) ;
141
+ stdin . setRawMode ( true ) ;
142
+
143
+ // NOTE: this `Promise.resolve().then(...)` is basically used to spawn a "co-routine" that is executed
144
+ // before the callback attached to the Promise returned by this function (and it allows the `run` function
145
+ // to not be stuck in the while loop).
146
+ Promise . resolve ( ) . then ( async function ( ) {
147
+ log . info ( 'Press R to reload (and Ctrl-C to quit)' ) ;
148
+
149
+ let userExit = false ;
150
+
151
+ while ( ! userExit ) {
152
+ const keyPressed = await new Promise ( ( resolve ) => {
153
+ stdin . once ( 'keypress' , ( str , key ) => resolve ( key ) ) ;
154
+ } ) ;
155
+
156
+ if ( keyPressed . ctrl && keyPressed . name === 'c' ) {
157
+ userExit = true ;
158
+ } else if ( keyPressed . name === 'r' && addonId ) {
159
+ log . debug ( 'Reloading extension on user request' ) ;
160
+ await addonReload ( { addonId, client} ) ;
161
+ }
162
+ }
163
+
164
+ log . info ( '\nExiting web-ext on user request' ) ;
165
+ firefoxProcess . kill ( ) ;
166
+ } ) ;
167
+ }
119
168
}
120
169
121
170
@@ -186,6 +235,7 @@ export type CmdRunOptions = {|
186
235
firefoxApp : typeof defaultFirefoxApp ,
187
236
firefoxClient : typeof defaultFirefoxClient ,
188
237
reloadStrategy : typeof defaultReloadStrategy ,
238
+ ExtensionRunnerClass ?: typeof ExtensionRunner ,
189
239
| } ;
190
240
191
241
export default async function run (
@@ -198,6 +248,7 @@ export default async function run(
198
248
firefoxApp = defaultFirefoxApp ,
199
249
firefoxClient = defaultFirefoxClient ,
200
250
reloadStrategy = defaultReloadStrategy ,
251
+ ExtensionRunnerClass = ExtensionRunner ,
201
252
} : CmdRunOptions = { } ) : Promise < Object > {
202
253
203
254
log . info ( `Running web extension from ${ sourceDir } ` ) ;
@@ -216,7 +267,7 @@ export default async function run(
216
267
217
268
const manifestData = await getValidatedManifest ( sourceDir ) ;
218
269
219
- const runner = new ExtensionRunner ( {
270
+ const runner = new ExtensionRunnerClass ( {
220
271
sourceDir,
221
272
firefoxApp,
222
273
firefox,
0 commit comments