Skip to content

Commit 0c4fe86

Browse files
vialohlmichelinLuke Hagerrushilsrivastava
authored
✨ MV3 + other misc improvements/fixes (#386)
* chore: lint * add compatibility with manifest V3 * 🐛 Use `cross-env` for all scripts * 🐛 Use JSON5 to parse manifest.json (closes #216) * 🐛 Fix type declarations (closes #209) * 📝 Updated README --------- Co-authored-by: Louis-Marie Michelin <lmichelin@outlook.fr> Co-authored-by: Luke Hager <luke@fullstacked.services> Co-authored-by: Rushil Srivastava <11495473+rushilsrivastava@users.noreply.github.com>
1 parent e951656 commit 0c4fe86

File tree

8 files changed

+73
-76
lines changed

8 files changed

+73
-76
lines changed

README.md

+36-40
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,41 @@ A Webpack plugin to automatically reload browser extensions during development.
99
<br>
1010
<br>
1111
</div>
12-
12+
1313
[![npm version](https://img.shields.io/npm/v/webpack-ext-reloader)](https://www.npmjs.com/package/webpack-ext-reloader)
1414
[![Test Status](https://github.com/SimplifyJobs/webpack-ext-reloader/workflows/tests/badge.svg)](https://github.com/SimplifyJobs/webpack-ext-reloader/actions?query=branch%3Amaster)
1515
[![Known Vulnerabilities](https://snyk.io/test/github/SimplifyJobs/webpack-ext-reloader/badge.svg)](https://snyk.io/test/github/SimplifyJobs/webpack-ext-reloader/)
1616
[![NPM Downloads](https://img.shields.io/npm/dt/webpack-ext-reloader.svg)](https://www.npmjs.com/package/webpack-ext-reloader)
1717

1818
## Installing
1919

20-
npm
20+
For npm:
2121

2222
```bash
2323
npm install webpack-ext-reloader --save-dev
2424
```
2525

26-
yarn
26+
For yarn:
2727

2828
```bash
2929
yarn add webpack-ext-reloader --dev
3030
```
3131

3232
## What is this?
3333

34-
This is a webpack plugin that allows you to bring hot reloading functionality to WebExtensions, essentially `webpack-dev-server`, but for (WebExtensions)[https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions].
34+
This is a webpack plugin that brings hot reloading functionality to WebExtensions, essentially resembling `webpack-dev-server` but for [WebExtensions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions).
3535

36-
This is a fork from [`webpack-extension-reloader`](https://github.com/rubenspgcavalcante/webpack-extension-reloader), maintained and updated by the team here at Simplify. The goal here is to continue to support the latest version of webpack (`webpack-extension-reloader` only supports webpack v4) while adding new improvements (i.e. HMR).
36+
This project is a fork of [`webpack-extension-reloader`](https://github.com/rubenspgcavalcante/webpack-extension-reloader), maintained and updated by the team at Simplify. The goal is to continue supporting the latest version of webpack (`webpack-extension-reloader` only supports webpack v4) while introducing new improvements, such as HMR.
3737

3838
![](.github/sample-gif.gif)
3939

40-
**Note**: This plugin doesn't support [**Hot Module Replacement (HMR)**](https://webpack.js.org/concepts/hot-module-replacement/) yet.
40+
**Note**: This plugin does not support [**Hot Module Replacement (HMR)**](https://webpack.js.org/concepts/hot-module-replacement/) yet.
4141

4242
## How to use
4343

4444
### Using as a plugin
4545

46-
Add `webpack-ext-reloader` to the plugins section of your webpack configuration file. Note that this plugin don't outputs the manifest (at most read it to gather information).
47-
For outputing not only the `manifest.json` but other static files too, use `CopyWebpackPlugin`.
46+
Add `webpack-ext-reloader` to the plugins section of your webpack configuration file. This plugin does not output the manifest; it might read it for information at most. For outputting not only the `manifest.json` but other static files as well, use `CopyWebpackPlugin`.
4847

4948
```js
5049
const ExtReloader = require('webpack-ext-reloader');
@@ -58,7 +57,7 @@ plugins: [
5857
]
5958
```
6059

61-
You can point to your `manifest.json file`...
60+
You can point to your `manifest.json` file...
6261

6362
```js
6463
plugins: [
@@ -81,7 +80,7 @@ module.exports = {
8180
background: './my-background-script.js',
8281
popup: 'popup',
8382
},
84-
//...
83+
// ...
8584
plugins: [
8685
new ExtReloader({
8786
port: 9090, // Which port use to create the server
@@ -97,89 +96,86 @@ module.exports = {
9796
}
9897
```
9998

100-
**Note I**: `entry` or `manifest` are needed. If both are given, entry will override the information comming from `manifest.json`. If none are given the default `entry` values (see above) are used.
99+
**Note I**: Either `entry` or `manifest` is needed. If both are provided, the `entry` will override the information from `manifest.json`. If neither is provided, the default `entry` values (as shown above) are used.
101100

102-
And then just run your application with Webpack in watch mode:
101+
Run your application with Webpack in watch mode:
103102

104103
```bash
105104
NODE_ENV=development webpack --config myconfig.js --mode=development --watch
106105
```
107106

108-
**Note II**: You need to set `--mode=development` to activate the plugin (only if you didn't set on the webpack.config.js already) then you need to run with `--watch`, as the plugin will be able to sign the extension only if webpack triggers the rebuild (again, only if you didn't set on webpack.config).
107+
**Note II**: You need to set `--mode=development` to activate the plugin. If you didn't set it in the webpack.config.js already, you need to run with `--watch` since the plugin will be able to sign the extension only if webpack triggers the rebuild.
109108

110109
### Multiple Content Script and Extension Page support
111110

112-
If you use more than one content script or extension page in your extension, like:
111+
If your extension uses more than one content script or extension page, like:
113112

114113
```js
115114
entry: {
116115
'my-first-content-script': './my-first-content-script.js',
117116
'my-second-content-script': './my-second-content-script.js',
118-
// and so on ...
119117
background: './my-background-script.js',
120118
'popup': './popup.js',
121119
'options': './options.js',
122-
// and so on ...
123120
}
124121
```
125122

126-
You can use the `entries.contentScript` or `entries.extensionPage` options as an array:
123+
Use the `entries.contentScript` or `entries.extensionPage` options as an array:
127124

128125
```js
129126
plugins: [
130127
new ExtReloader({
131128
entries: {
132-
contentScript: ['my-first-content-script', 'my-second-content-script', /* and so on ... */],
129+
contentScript: ['my-first-content-script', 'my-second-content-script'],
133130
background: 'background',
134-
extensionPage: ['popup', 'options', /* and so on ... */],
131+
extensionPage: ['popup', 'options'],
135132
}
136133
}),
137-
// ...
138134
]
139135
```
140136

141137
### CLI
142138

143-
If you don't want all the plugin setup, you can just use the client that comes with the package.
144-
You can use by installing the package globally, or directly using `npx`:
139+
If you'd rather skip the plugin setup, you can use the client included with the package. Install the package globally or use `npx`:
145140

146141
```bash
147142
npx webpack-ext-reloader
148143
```
149144

150-
If you run directly, it will use the default configurations, but if you want to customize
151-
you can call it with the following options:
145+
If run directly, it will use the default configurations. But if you'd like customization:
152146

153147
```bash
154148
npx webpack-ext-reloader --config wb.config.js --port 9080 --no-page-reload --content-script my-content.js --background bg.js --extension-page popup.js
155149
```
156150

157-
If you have **multiple** content scripts or extension pages, just use comma (with no spaces) while passing the option
151+
For **multiple** content scripts or extension pages, separate the options with commas (without spaces):
158152

159153
```bash
160-
npx webpack-ext-reloader --content-script my-first-content.js,my-second-content.js,my-third-content.js --extension-page popup.js,options.js
154+
npx webpack-ext-reloader --content-script my-first-content.js,my-second-content.js --extension-page popup.js,options.js
161155
```
162156

163157
### Client options
164158

165-
| name | default | description |
166-
| ---------------- | ----------------- | ----------------------------------------------------------------- |
167-
| --help | | Shows this help |
168-
| --config | webpack.config.js | The webpack configuration file path |
169-
| --port | 9090 | The port to run the server |
170-
| --manifest | | The path to the extension **manifest.json** file |
171-
| --content-script | content-script | The **entry/entries** name(s) for the content script(s) |
172-
| --background | background | The **entry** name for the background script |
173-
| --extension-page | popup | The **entry/entries** name(s) for the extension pages(s) |
174-
| --no-page-reload | | Disable the auto reloading of all **pages** which runs the plugin |
159+
| Name | Default | Description |
160+
| ----------------- | ----------------- | ----------------------------------------------------------------- |
161+
| --help | | Shows help |
162+
| --config | webpack.config.js | Path to the webpack configuration file |
163+
| --port | 9090 | Port to run the server on |
164+
| --manifest | | Path to the extension's **manifest.json** file |
165+
| --content-script | content-script | **Entry/entries** name(s) for the content script(s) |
166+
| --background | background | **Entry** name for the background script |
167+
| --extension-page | popup | **Entry/entries** name(s) for the extension page(s) |
168+
| --no-page-reload | | Disable auto-reloading of all **pages** running the plugin |
175169

176-
Every time content or background scripts are modified, the extension is reloaded :)
177-
**Note:** the plugin only works on **development** mode, so don't forget to set the NODE_ENV before run the command above
170+
Whenever content or background scripts are modified, the extension will reload.
171+
**Note**: This plugin only works in **development** mode. Remember to set the NODE_ENV before running the commands above.
178172

179173
### Contributing
180174

181-
Please before opening any **issue** or **pull request** check the [contribution guide](/.github/CONTRIBUTING.MD).
175+
Before opening any **issue** or **pull request**, please refer to the [contribution guide](/.github/CONTRIBUTING.MD).
182176

183177
### License
184178

185-
This project has been forked from [rubenspgcavalcante/webpack-extension-reloader](https://github.com/rubenspgcavalcante/webpack-extension-reloader), which is licensed under the [MIT license](https://github.com/rubenspgcavalcante/webpack-extension-reloader/blob/master/LICENSE). All changes made in this fork have been licensed via the [MIT license](https://github.com/SimplifyJobs/webpack-ext-reloader/blob/master/LICENSE).
179+
This project is a fork from [rubenspgcavalcante/webpack-extension-reloader](https://github.com/rubenspgcavalcante/webpack-extension-reloader), which is licensed under the [MIT license](https://github.com/rubenspgcavalcante/webpack-extension-reloader/blob/master/LICENSE
180+
181+
). All modifications made in this fork are also licensed under the [MIT license](https://github.com/SimplifyJobs/webpack-ext-reloader/blob/master/LICENSE).

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
]
2424
},
2525
"scripts": {
26-
"build": "NODE_ENV=production webpack",
27-
"test": "NODE_ENV=test webpack && mocha dist/tests.js",
28-
"analyze": "NODE_ENV=production webpack --env.analyze",
29-
"start:dev": "NODE_ENV=development webpack --watch",
26+
"build": "cross-env NODE_ENV=production webpack",
27+
"test": "cross-env NODE_ENV=test webpack && mocha dist/tests.js",
28+
"analyze": "cross-env NODE_ENV=production webpack --env.analyze",
29+
"start:dev": "cross-env NODE_ENV=development webpack --watch",
3030
"start:sample:chrome": "cross-env NODE_ENV=development cross-env TARGET_BROWSER=chrome webpack --config sample/webpack.plugin.js --watch",
3131
"start:sample:firefox": "cross-env NODE_ENV=development cross-env TARGET_BROWSER=firefox webpack --config sample/webpack.plugin.js --watch",
3232
"prepublishOnly": "npm run build",
@@ -56,6 +56,7 @@
5656
"clean-webpack-plugin": "^4.0.0",
5757
"colors": "^1.4.0",
5858
"cross-env": "^7.0.3",
59+
"json5": "^2.2.3",
5960
"lodash": "^4.17.21",
6061
"minimist": "^1.2.6",
6162
"useragent": "^2.3.0",

src/messages/errors.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ the provided on 'manifest.json' or 'entry.background' \
1010
option of the plugin",
1111
);
1212

13-
export const bgScriptManifestRequiredMsg = new Message(ERROR, 2, "Background script on manifest is required");
13+
export const bgScriptManifestRequiredMsg = new Message(
14+
ERROR,
15+
2,
16+
"Background script or service worker on manifest is required",
17+
);

src/middleware/wer-middleware.raw.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/* This will be converted into a lodash templ., any */
66
/* external argument must be provided using it */
77
/* -------------------------------------------------- */
8-
(function(window) {
8+
(function() {
99

1010
const injectionContext = this || window || {browser: null};
1111

@@ -71,7 +71,9 @@
7171
if (type === SIGN_CHANGE && (!payload || !payload.onlyPageChanged)) {
7272
tabs.query({ status: "complete" }).then(loadedTabs => {
7373
loadedTabs.forEach(
74-
tab => tab.id && tabs.sendMessage(tab.id, { type: SIGN_RELOAD }),
74+
// in MV3 tabs.sendMessage returns a Promise and we need to catch the errors
75+
// https://groups.google.com/a/chromium.org/g/chromium-extensions/c/st_Nh7j3908/m/1muOgSX5AwAJ
76+
tab => tab.id && tabs.sendMessage(tab.id, { type: SIGN_RELOAD })?.catch(() => null),
7577
);
7678
socket.send(
7779
JSON.stringify({
@@ -137,9 +139,10 @@
137139

138140
// ======================= Bootstraps the middleware =========================== //
139141
runtime.reload
140-
? extension.getBackgroundPage() === window ? backgroundWorker(new WebSocket(wsHost)) : extensionPageWorker()
142+
// in MV3 background service workers don't have access to the DOM
143+
? (typeof window === 'undefined' || extension.getBackgroundPage() === window) ? backgroundWorker(new WebSocket(wsHost)) : extensionPageWorker()
141144
: contentScriptWorker();
142-
})(window);
145+
})();
143146

144147
/* ----------------------------------------------- */
145148
/* End of Webpack Hot Extension Middleware */

src/utils/manifest.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { readFileSync } from "fs";
22
import { flatMapDeep } from "lodash";
3+
import JSON5 from "json5";
34
import { Compiler, Entry } from "webpack";
45
import { bgScriptEntryErrorMsg, bgScriptManifestRequiredMsg } from "../messages/errors";
56

@@ -8,19 +9,19 @@ export function extractEntries(
89
manifestPath: string,
910
webpackOutput: Compiler["options"]["output"] = {},
1011
): IEntriesOption {
11-
const manifestJson = JSON.parse(readFileSync(manifestPath).toString()) as IExtensionManifest;
12-
const { background, content_scripts } = manifestJson;
12+
const manifestJson = JSON5.parse(readFileSync(manifestPath).toString()) as IExtensionManifest;
13+
const { background, content_scripts: contentScripts } = manifestJson;
1314
const { filename } = webpackOutput;
1415

1516
if (!filename) {
1617
throw new Error("Please specify the `output.filename` in your webpack config.");
1718
}
1819

19-
if (!background?.scripts) {
20+
if (!(background?.scripts || background?.service_worker)) {
2021
throw new TypeError(bgScriptManifestRequiredMsg.get());
2122
}
2223

23-
const bgScriptFileNames = background.scripts;
24+
const bgScriptFileNames = background.service_worker ? [background.service_worker] : background.scripts ?? [];
2425
const toRemove = (filename as string).replace("[name]", "");
2526

2627
const bgWebpackEntry = Object.keys(webpackEntry).find((entryName) =>
@@ -31,9 +32,9 @@ export function extractEntries(
3132
throw new TypeError(bgScriptEntryErrorMsg.get());
3233
}
3334

34-
const contentEntries: unknown = content_scripts
35+
const contentEntries: unknown = contentScripts
3536
? flatMapDeep(Object.keys(webpackEntry), (entryName) =>
36-
content_scripts.map(({ js }) =>
37+
contentScripts.map(({ js }) =>
3738
js.map((contentItem) => contentItem.replace(toRemove, "")).filter((contentItem) => contentItem === entryName),
3839
),
3940
)

typings/declarations.d.ts

+4-18
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ declare interface IMiddlewareTemplateParams {
1010
reloadPage: boolean;
1111
}
1212

13-
declare type InjectMiddleware = (
14-
assets: Record<string, any>,
15-
chunks: Set<any>,
16-
) => Record<string, any>;
13+
declare type InjectMiddleware = (assets: Record<string, any>, chunks: Set<any>) => Record<string, any>;
1714

1815
declare type MiddlewareInjector = (
1916
{ background, contentScript, extensionPage }: IEntriesOption,
@@ -22,10 +19,7 @@ declare type MiddlewareInjector = (
2219

2320
declare type Triggerer = (onlyPageChanged: boolean) => Promise<any>;
2421

25-
declare type TriggererFactory = (
26-
port: number,
27-
reloadPage: boolean,
28-
) => Triggerer;
22+
declare type TriggererFactory = (port: number, reloadPage: boolean) => Triggerer;
2923

3024
declare type VersionPair = [number | undefined, number | undefined];
3125

@@ -45,13 +39,7 @@ declare type LOG_WARN = 3;
4539
declare type LOG_ERROR = 4;
4640
declare type LOG_DEBUG = 5;
4741

48-
declare type LOG_LEVEL =
49-
| LOG_NONE
50-
| LOG_LOG
51-
| LOG_INFO
52-
| LOG_WARN
53-
| LOG_ERROR
54-
| LOG_DEBUG;
42+
declare type LOG_LEVEL = LOG_NONE | LOG_LOG | LOG_INFO | LOG_WARN | LOG_ERROR | LOG_DEBUG;
5543

5644
declare interface IWebpackChunk {
5745
files: string[];
@@ -73,13 +61,11 @@ declare interface IExtensionManifest {
7361
background?: {
7462
page?: string;
7563
scripts?: string[];
64+
service_worker?: string;
7665
};
7766
icons?: {
7867
[key: string]: string;
7968
};
80-
browser_action?: {
81-
default_popup: string;
82-
};
8369
content_scripts?: [
8470
{
8571
matches: string[];

typings/webpack-ext-reloader.d.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@ export interface IExtensionReloaderInstance {
1111
apply(compiler: Compiler): void;
1212
}
1313

14-
export type ExtensionReloader = new (options?: IPluginOptions) => IExtensionReloaderInstance;
14+
export declare class ExtensionReloader implements IExtensionReloaderInstance {
15+
constructor(options?: IPluginOptions);
1516

16-
declare module "webpack-extension-reloader" {
17+
apply(compiler: Compiler): void;
18+
}
19+
20+
export default ExtensionReloader;
21+
22+
declare module "webpack-ext-reloader" {
1723
export default ExtensionReloader;
1824
export = IExtensionReloaderInstance;
1925
}

0 commit comments

Comments
 (0)