Skip to content

Commit

Permalink
Add page entry (#37)
Browse files Browse the repository at this point in the history
* Create .gitattributes

* Reload extension pages only if they are the only webpack entry to change.

* Formatting.

* Update popup.js

* Link to the correct file.

* Added tests for extensionPage.

* Added popup as default extension page entry.

* Update manifest.ts

* Use entries instead of manifest.

* Update manual.ts

* Format

* Format.

* Updated READMEs.
  • Loading branch information
kadauchi authored and rubenspgcavalcante committed Sep 13, 2019
1 parent e356f9b commit d5e3cf7
Show file tree
Hide file tree
Showing 19 changed files with 191 additions and 63 deletions.
Empty file added .gitattributes
Empty file.
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,17 @@ module.exports = {
entry: {
'content-script': './my-content-script.js',
background: './my-background-script.js'
popup: 'popup',
},
//...
plugins: [
new ExtensionReloader({
port: 9090, // Which port use to create the server
reloadPage: true, // Force the reload of the page also
entries: { // The entries used for the content/background scripts
entries: { // The entries used for the content/background scripts or extension pages
contentScript: 'content-script',
background: 'background'
background: 'background',
extensionPage: 'popup',
}
})
]
Expand All @@ -93,24 +95,28 @@ NODE_ENV=development webpack --config myconfig.js --mode=development --watch

**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).

### Multiple Content Script support
If you use more than one content script in your extension, like:
### Multiple Content Script and Extension Page support
If you use more than one content script or extension page in your extension, like:
```js
entry: {
'my-first-content-script': './my-first-content-script.js',
'my-second-content-script': './my-second-content-script.js',
// and so on ...
background: './my-background-script.js'
background: './my-background-script.js',
'popup': './popup.js',
'options': './options.js',
// and so on ...
}
```

You can use the `entries.contentScript` options as an array:
You can use the `entries.contentScript` or `entries.extensionPage` options as an array:
```js
plugins: [
new ExtensionReloader({
entries: {
contentScript: ['my-first-content-script', 'my-second-content-script', /* and so on ... */],
background: 'background'
background: 'background',
extensionPage: ['popup', 'options', /* and so on ... */],
}
})
]
Expand All @@ -126,11 +132,11 @@ npx webpack-extension-reloader
If you run directly, it will use the default configurations, but if you want to customize
you can call it with the following options:
```bash
npx webpack-extension-reloader --config wb.config.js --port 9080 --no-page-reload --content-script my-content.js --background bg.js
npx webpack-extension-reloader --config wb.config.js --port 9080 --no-page-reload --content-script my-content.js --background bg.js --extension-page popup.js
```
If you have **multiple** content scripts, just use comma (with no spaces) while passing the option
If you have **multiple** content scripts or extension pages, just use comma (with no spaces) while passing the option
```bash
npx webpack-extension-reloader --content-script my-first-content.js,my-second-content.js,my-third-content.js
npx webpack-extension-reloader --content-script my-first-content.js,my-second-content.js,my-third-content.js --extension-page popup.js,options.js
```

### Client options
Expand All @@ -143,6 +149,7 @@ npx webpack-extension-reloader --content-script my-first-content.js,my-second-co
| --manifest | | The path to the extension **manifest.json** file |
| --content-script | content-script | The **entry/entries** name(s) for the content script(s) |
| --background | background | The **entry** name for the background script |
| --extension-page | popup | The **entry/entries** name(s) for the extension pages(s) |
| --no-page-reload | | Disable the auto reloading of all **pages** which runs the plugin |

Every time content or background scripts are modified, the extension is reloaded :)
Expand Down
8 changes: 6 additions & 2 deletions client/manual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import {
DEFAULT_CONFIG,
DEFAULT_PORT,
DEFAULT_CONTENT_SCRIPT_ENTRY,
DEFAULT_BACKGROUND_ENTRY
DEFAULT_BACKGROUND_ENTRY,
DEFAULT_EXTENSION_PAGE_ENTRY
} from "../src/constants/options.constants";

export default () => `
Usage:
wer [--config <config_path>] [--port <port_number>] [--no-page-reload] [--content-script <content_script_paths>] [--background <bg_script_path>]
wer [--config <config_path>] [--port <port_number>] [--no-page-reload] [--content-script <content_script_paths>] [--background <bg_script_path>] [--extension-page <extension_page_paths>]
Complete API:
+------------------------------------------------------------------------------------------------------------+
Expand All @@ -26,6 +27,9 @@ Complete API:
| --background | ${
DEFAULT_BACKGROUND_ENTRY
} | The **entry** name for the background script |
| --extension-page | ${
DEFAULT_EXTENSION_PAGE_ENTRY
} | The **entry/entries** name(s) for the extension pages(s) |
| --manifest | null | The **manifest.json** path |
| --no-page-reload | | Disable the auto reloading of all **pages** which runs the plugin |
+------------------------------------------------------------------------------------------------------------+
Expand Down
1 change: 1 addition & 0 deletions sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Open a new tab on any site (can't be the home page), open the debugger and you'r
```
Change anything inside `plugin-src` and look the page reload it automatically, using the new version of your extension.
Tip: try to change the content of the console log within the `my-content-script`, and see the page reload and show the new result.
Tip: try to change the content of the console log within the `popup`, and see the popup reload and show the new result without reloading the entire extension.

## Why can't I load plugin-src/ dir?
The source needs to be parsed and bundled by Webpack, then is outputted on the `dist` directory. This means
Expand Down
2 changes: 1 addition & 1 deletion sample/plugin-src/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
<html>
<body>
<button id="button">Click here!</button>
<script src="popup.js"></script>
<script src="popup.bundle.js"></script>
</body>
</html>
6 changes: 2 additions & 4 deletions sample/plugin-src/popup.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/**
* Neither this file or any dependency (like style.css) should trigger
* the plugin reload. This way the popup will not close clearing it state
*/
console.info("Change anything here!");

import "./style.css";

const element = document.createElement("span");
Expand Down
11 changes: 4 additions & 7 deletions sample/webpack.plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ module.exports = {
entry: {
"content-script": "./sample/plugin-src/my-content-script.js",
background: "./sample/plugin-src/my-background.js",

// This is just the popup script, it shouldn't trigger the plugin reload when is changed
popup: "./sample/plugin-src/popup.js"
},
output: {
Expand All @@ -25,14 +23,13 @@ module.exports = {
/* By default the plugin will work only when NODE_ENV is "development" */
/***********************************************************************/
new ExtensionReloaderPlugin({
/*
// Also possible to use
entries: {
contentScript: 'content-script',
background: 'background'
background: 'background',
extensionPage: 'popup'
}
*/
manifest: resolve(__dirname, "manifest.json")
// Also possible to use
// manifest: resolve(__dirname, "manifest.json")
}),

new MiniCssExtractPlugin({ filename: "style.css" }),
Expand Down
68 changes: 67 additions & 1 deletion specs/middleware-injector.specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,34 @@ describe("middleware-injector", () => {
extraContentScript: {
name: "extraContentChunkName",
path: "./path/to/extra-content-script.js"
},
extensionPage: {
name: "pageChunkName",
path: "./path/to/popup.js"
},
extraExtensionPage: {
name: "extraPageChunkName",
path: "./path/to/options.js"
}
};

const templateOpts = { port: 1234, reloadPage: true };

const options: EntriesOption = {
background: entriesInfo.background.name,
contentScript: entriesInfo.contentScript.name
contentScript: entriesInfo.contentScript.name,
extensionPage: entriesInfo.extensionPage.name
};

const options2: EntriesOption = {
background: entriesInfo.background.name,
contentScript: [
entriesInfo.contentScript.name,
entriesInfo.extraContentScript.name
],
extensionPage: [
entriesInfo.extensionPage.name,
entriesInfo.extraExtensionPage.name
]
};

Expand All @@ -53,6 +66,10 @@ describe("middleware-injector", () => {
[entriesInfo.extraContentScript.path]: {
source: () => "const extraCs = true;"
},
[entriesInfo.extensionPage.path]: { source: () => "const ep = true;" },
[entriesInfo.extraExtensionPage.path]: {
source: () => "const extraEp = true;"
},
[fakeCssPath]: { source: () => "some-css-source" },
[fakeImgPath]: { source: () => "some-base64-source" }
};
Expand All @@ -64,10 +81,15 @@ describe("middleware-injector", () => {
name: options.contentScript,
files: [entriesInfo.contentScript.path, fakeCssPath]
},
{
name: options.extensionPage,
files: [entriesInfo.extensionPage.path, fakeCssPath]
},
{ name: "someOtherAsset", files: [fakeImgPath] }
];

const [firstContent, secondContent] = <string[]>options2.contentScript;
const [firstPage, secondPage] = <string[]>options2.extensionPage;

multipleContentsChunks = [
{ name: options2.background, files: [entriesInfo.background.path] },
Expand All @@ -83,6 +105,18 @@ describe("middleware-injector", () => {
name: secondContent,
files: [entriesInfo.extraContentScript.path]
},
{
name: options2.extensionPage,
files: [entriesInfo.extensionPage.path, fakeCssPath]
},
{
name: firstPage,
files: [entriesInfo.extensionPage.path, fakeCssPath]
},
{
name: secondPage,
files: [entriesInfo.extraExtensionPage.path]
},
{ name: "someOtherAsset", files: [fakeImgPath] }
];
});
Expand Down Expand Up @@ -138,6 +172,38 @@ describe("middleware-injector", () => {
assert.include(newSecondContentSource, oldSecondContentSource);
assert.include(newSecondContentSource, sourceCode);
});

it("Should inject into a single extensionPage", () => {
const newContentSource = assetsSingleContent[
entriesInfo.extensionPage.path
].source();
const oldContentSource = assets[entriesInfo.extensionPage.path].source();
assert.include(newContentSource, oldContentSource);
assert.include(newContentSource, sourceCode);
});

it("Should inject into the multiple extensionPages", () => {
const newFirstContentSource = assetsMultiContent[
entriesInfo.extensionPage.path
].source();
const oldFirstContentSource = assets[
entriesInfo.extensionPage.path
].source();

assert.include(newFirstContentSource, oldFirstContentSource);
assert.include(newFirstContentSource, sourceCode);

const newSecondContentSource = assetsMultiContent[
entriesInfo.extraExtensionPage.path
].source();

const oldSecondContentSource = assets[
entriesInfo.extraExtensionPage.path
].source();

assert.include(newSecondContentSource, oldSecondContentSource);
assert.include(newSecondContentSource, sourceCode);
});
});

it("Should return only changed assets", () => {
Expand Down
57 changes: 39 additions & 18 deletions src/ExtensionReloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,44 @@ export default class ExtensionReloaderImpl extends AbstractPluginReloader
return false;
}

_contentOrBgChanged(
_whatChanged(
chunks: WebpackChunk[],
{ background, contentScript }: EntriesOption
{ background, contentScript, extensionPage }: EntriesOption
) {
return chunks
.filter(({ name, hash }) => {
const oldVersion = this._chunkVersions[name];
this._chunkVersions[name] = hash;
return hash !== oldVersion;
})
.some(({ name }) => {
let contentChanged = false;
const bgChanged = name === background;

if (Array.isArray(contentScript)) {
contentChanged = contentScript.some(script => script === name);
const changedChunks = chunks.filter(({ name, hash }) => {
const oldVersion = this._chunkVersions[name];
this._chunkVersions[name] = hash;
return hash !== oldVersion;
});

const contentOrBgChanged = changedChunks.some(({ name }) => {
let contentChanged = false;
const bgChanged = name === background;

if (Array.isArray(contentScript)) {
contentChanged = contentScript.some(script => script === name);
} else {
contentChanged = name === contentScript;
}

return contentChanged || bgChanged;
});

const onlyPageChanged =
!contentOrBgChanged &&
changedChunks.some(({ name }) => {
let pageChanged = false;

if (Array.isArray(extensionPage)) {
pageChanged = extensionPage.some(script => script === name);
} else {
contentChanged = name === contentScript;
pageChanged = name === extensionPage;
}

return contentChanged || bgChanged;
return pageChanged;
});

return { contentOrBgChanged, onlyPageChanged };
}

_registerPlugin(compiler: Compiler) {
Expand Down Expand Up @@ -81,8 +97,13 @@ export default class ExtensionReloaderImpl extends AbstractPluginReloader
});

this._eventAPI.afterEmit((comp, done) => {
if (this._contentOrBgChanged(comp.chunks, parsedEntries)) {
this._triggerer()
const { contentOrBgChanged, onlyPageChanged } = this._whatChanged(
comp.chunks,
parsedEntries
);

if (contentOrBgChanged || onlyPageChanged) {
this._triggerer(onlyPageChanged)
.then(done)
.catch(done);
}
Expand Down
1 change: 1 addition & 0 deletions src/constants/options.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const DEFAULT_CONFIG = "webpack.config.js";
export const DEFAULT_RELOAD_PAGE = true;
export const DEFAULT_CONTENT_SCRIPT_ENTRY = "content-script";
export const DEFAULT_BACKGROUND_ENTRY = "background";
export const DEFAULT_EXTENSION_PAGE_ENTRY = "popup";
4 changes: 2 additions & 2 deletions src/hot-reload/HotReloaderServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export default class HotReloaderServer {
});
}

signChange(reloadPage: boolean): Promise<any> {
signChange(reloadPage: boolean, onlyPageChanged: boolean): Promise<any> {
if (this._signEmitter) {
return this._signEmitter.safeSignChange(reloadPage);
return this._signEmitter.safeSignChange(reloadPage, onlyPageChanged);
} else return Promise.resolve(null);
}
}
13 changes: 9 additions & 4 deletions src/hot-reload/SignEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,21 @@ export default class SignEmitter {
}
}

safeSignChange(reloadPage: boolean): Promise<any> {
safeSignChange(reloadPage: boolean, onlyPageChanged: boolean): Promise<any> {
return new Promise((res, rej) => {
this._safeSignChange(reloadPage, res, rej);
this._safeSignChange(reloadPage, onlyPageChanged, res, rej);
});
}

private _setupSafeSignChange() {
return (reloadPage: boolean, onSuccess: Function, onError: Function) => {
return (
reloadPage: boolean,
onlyPageChanged: boolean,
onSuccess: Function,
onError: Function
) => {
try {
this._sendMsg(signChange({ reloadPage }));
this._sendMsg(signChange({ reloadPage, onlyPageChanged }));
onSuccess();
} catch (err) {
onError(err);
Expand Down
Loading

0 comments on commit d5e3cf7

Please sign in to comment.