Skip to content

Commit 97404a9

Browse files
saintsebastiankumar303
authored andcommitted
feat: Added reload from the terminal by pressing a key (#746)
1 parent 740e051 commit 97404a9

File tree

5 files changed

+198
-65
lines changed

5 files changed

+198
-65
lines changed

src/cmd/run.js

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
/* @flow */
2+
import readline from 'readline';
3+
import tty from 'tty';
4+
25
import type FirefoxProfile from 'firefox-profile';
36
import type Watchpack from 'watchpack';
47

@@ -41,18 +44,17 @@ export type WatcherCreatorParams = {|
4144
sourceDir: string,
4245
artifactsDir: string,
4346
onSourceChange?: OnSourceChangeFn,
44-
desktopNotifications?: typeof defaultDesktopNotifications,
4547
ignoreFiles?: Array<string>,
4648
createFileFilter?: FileFilterCreatorFn,
49+
addonReload: typeof defaultAddonReload,
4750
|};
4851

4952
export type WatcherCreatorFn = (params: WatcherCreatorParams) => Watchpack;
5053

5154
export function defaultWatcherCreator(
5255
{
53-
addonId, client, sourceDir, artifactsDir, ignoreFiles,
56+
addonId, addonReload, client, sourceDir, artifactsDir, ignoreFiles,
5457
onSourceChange = defaultSourceWatcher,
55-
desktopNotifications = defaultDesktopNotifications,
5658
createFileFilter = defaultFileFilterCreator,
5759
}: WatcherCreatorParams
5860
): Watchpack {
@@ -62,24 +64,37 @@ export function defaultWatcherCreator(
6264
return onSourceChange({
6365
sourceDir,
6466
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}),
7968
shouldWatchFile: (file) => fileFilter.wantFile(file),
8069
});
8170
}
8271

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+
8398

8499
// defaultReloadStrategy types and implementation.
85100

@@ -94,8 +109,10 @@ export type ReloadStrategyParams = {|
94109
|};
95110

96111
export type ReloadStrategyOptions = {|
112+
addonReload?: typeof defaultAddonReload,
97113
createWatcher?: WatcherCreatorFn,
98114
createFileFilter?: FileFilterCreatorFn,
115+
stdin: stream$Readable,
99116
|};
100117

101118
export function defaultReloadStrategy(
@@ -104,18 +121,50 @@ export function defaultReloadStrategy(
104121
sourceDir, artifactsDir, ignoreFiles,
105122
}: ReloadStrategyParams,
106123
{
124+
addonReload = defaultAddonReload,
107125
createWatcher = defaultWatcherCreator,
126+
stdin = process.stdin,
108127
}: ReloadStrategyOptions = {}
109128
): 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+
});
113132

114133
firefoxProcess.on('close', () => {
115134
client.disconnect();
116135
watcher.close();
136+
stdin.pause();
117137
});
118138

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+
}
119168
}
120169

121170

@@ -186,6 +235,7 @@ export type CmdRunOptions = {|
186235
firefoxApp: typeof defaultFirefoxApp,
187236
firefoxClient: typeof defaultFirefoxClient,
188237
reloadStrategy: typeof defaultReloadStrategy,
238+
ExtensionRunnerClass?: typeof ExtensionRunner,
189239
|};
190240

191241
export default async function run(
@@ -198,6 +248,7 @@ export default async function run(
198248
firefoxApp = defaultFirefoxApp,
199249
firefoxClient = defaultFirefoxClient,
200250
reloadStrategy = defaultReloadStrategy,
251+
ExtensionRunnerClass = ExtensionRunner,
201252
}: CmdRunOptions = {}): Promise<Object> {
202253

203254
log.info(`Running web extension from ${sourceDir}`);
@@ -216,7 +267,7 @@ export default async function run(
216267

217268
const manifestData = await getValidatedManifest(sourceDir);
218269

219-
const runner = new ExtensionRunner({
270+
const runner = new ExtensionRunnerClass({
220271
sourceDir,
221272
firefoxApp,
222273
firefox,

src/firefox/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export type FirefoxRunnerParams = {|
9696
export interface FirefoxProcess extends events$EventEmitter {
9797
stderr: events$EventEmitter;
9898
stdout: events$EventEmitter;
99+
kill: Function;
99100
}
100101

101102
export type FirefoxRunnerResults = {|

tests/unit/helpers.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ export function makeSureItFails(): Function {
149149
*
150150
*/
151151
// $FLOW_IGNORE: fake can return any kind of object and fake a defined set of methods for testing.
152-
export function fake<T>(original: Object, methods: Object = {}): T {
152+
export function fake<T>(
153+
original: Object, methods: Object = {}, skipProperties: Array<string> = []
154+
): T {
153155
var stub = {};
154156

155157
// Provide stubs for all original members:
@@ -162,7 +164,8 @@ export function fake<T>(original: Object, methods: Object = {}): T {
162164

163165
var proto = Object.getPrototypeOf(original);
164166
for (const key of props) {
165-
if (!original.hasOwnProperty(key) && !proto.hasOwnProperty(key)) {
167+
if (skipProperties.indexOf(key) >= 0 ||
168+
(!original.hasOwnProperty(key) && !proto.hasOwnProperty(key))) {
166169
continue;
167170
}
168171
const definition = original[key] || proto[key];
@@ -190,6 +193,10 @@ export function fake<T>(original: Object, methods: Object = {}): T {
190193
return stub;
191194
}
192195

196+
export function createFakeProcess() {
197+
return fake(process, {}, ['EventEmitter', 'stdin']);
198+
}
199+
193200

194201
/*
195202
* Returns a fake Firefox client as would be returned by

0 commit comments

Comments
 (0)