Skip to content
This repository has been archived by the owner on May 6, 2019. It is now read-only.

Commit

Permalink
Adding multiple content script support (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenspgcavalcante committed May 22, 2018
1 parent 419e43c commit 91f0cf9
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 27 deletions.
32 changes: 30 additions & 2 deletions README.MD
Expand Up @@ -51,6 +51,29 @@ And then just run your application with Webpack in watch mode:
NODE_ENV=development webpack --config myconfig.js --watch
```

### Multiple Content Script support
If you use more than one content script 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'
}
```

You can use the `entries.contentScript` options as an array:
```js
plugins: [
new ChromeExtensionReloader({
entries: {
contentScript: ['my-first-content-script', 'my-second-content-script', /* and so on ... */],
background: 'background'
}
})
]
```

### Using the CLI (still in Beta)
If you don't want all the plugin setup, you can just use the client that comes with the package:
```bash
Expand All @@ -59,15 +82,20 @@ wcer
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
wcer --config wb.config.js --port 9080 --no-page-reload content-script my-content.js --background bg.js
wcer --config wb.config.js --port 9080 --no-page-reload --content-script my-content.js --background bg.js
```
If you have **multiple** content scripts, just use comma (with no spaces) while passing the option
```bash
wcer --content-script my-first-content.js,my-second-content.js,my-third-content.js
```

### Client options

| name | default | description |
|--------------------|-------------------|-------------------------------------------------------------------|
| --config | webpack.config.js | The webpack configuration file path |
| --port | 9090 | The port to run the server |
| --content-script | content-script | The **entry** name for the content script |
| --content-script | content-script | The **entry/entries** name(s) for the content script(s) |
| --background | background | The **entry** name for the background script |
| --no-page-reload | | Disable the auto reloading of all **pages** which runs the plugin |

Expand Down
8 changes: 5 additions & 3 deletions client/index.ts
Expand Up @@ -24,10 +24,12 @@ const { _, ...params } = minimist(process.argv.slice(2));

const config = params[CONFIG] || DEFAULT_CONFIG;
const port = params[PORT] || DEFAULT_PORT;
const contentScript =
params[CONTENT_SCRIPT_ENTRY] || DEFAULT_CONTENT_SCRIPT_ENTRY;
const background = params[BACKGROUND_ENTRY] || DEFAULT_BACKGROUND_ENTRY;
const contentArg = params[CONTENT_SCRIPT_ENTRY] || DEFAULT_CONTENT_SCRIPT_ENTRY;
const contentScript = contentArg.includes(",")
? contentArg.split(",")
: contentArg;

const background = params[BACKGROUND_ENTRY] || DEFAULT_BACKGROUND_ENTRY;
const pluginOptions: PluginOptions = {
port,
reloadPage: !params[NO_PAGE_RELOAD],
Expand Down
65 changes: 50 additions & 15 deletions specs/middleware-injector.specs.ts
Expand Up @@ -4,20 +4,26 @@ import {stub} from "sinon";
import middlewareInjector from "../src/utils/middleware-injector";

describe("middleware-injector", () => {
let assetsBuilder, chunks;
let assetsBuilder, singleContentChunks, multipleContentsChunks;
const sourceCode = "console.log('I am a middleware!!!')";
const sourceFactory = stub().callsFake(
(toConcat: string, file) => ({source: () => toConcat + file.source()})
);

const entriesInfo = {
background: {name: "bgChunkName", path: "./path/to/bg-script.js"},
contentScript: {name: "contentChunkName", path: "./path/to/content-script.js"}
contentScript: {name: "contentChunkName", path: "./path/to/content-script.js"},
extraContentScript: {name: "extraContentChunkName", path: "./path/to/extra-content-script.js"}
};

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

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

const fakeCssPath = "./path/to/some.css";
Expand All @@ -26,34 +32,63 @@ describe("middleware-injector", () => {
const assets = {
[entriesInfo.background.path]: {source: () => "const bg = true;"},
[entriesInfo.contentScript.path]: {source: () => "const cs = true;"},
[entriesInfo.extraContentScript.path]: {source: () => "const extraCs = true;"},
[fakeCssPath]: {source: () => "some-css-source"},
[fakeImgPath]: {source: () => "some-base64-source"}
};

beforeEach(() => {
assetsBuilder = middlewareInjector(options, sourceCode, sourceFactory);

chunks = [
singleContentChunks = [
{name: options.background, files: [entriesInfo.background.path]},
{name: options.contentScript, files: [entriesInfo.contentScript.path, fakeCssPath]},
{name: "someOtherAsset", files: [fakeImgPath]}
];

multipleContentsChunks = [
{name: options2.background, files: [entriesInfo.background.path]},
{name: options2.contentScript, files: [entriesInfo.contentScript.path, fakeCssPath]},
{name: options2.contentScript[0], files: [entriesInfo.contentScript.path, fakeCssPath]},
{name: options2.contentScript[1], files: [entriesInfo.extraContentScript.path]},
{name: "someOtherAsset", files: [fakeImgPath]}
]
});

it("Should find the background and content script entries and inject the middleware source on them", () => {
const newAssets = assetsBuilder(assets, chunks, sourceFactory);
describe("Injecting middleware into background and content script entries", () => {
let assetsSingleContent, assetsMultiContent;
beforeEach(() => {
assetsBuilder = middlewareInjector(options, sourceCode, sourceFactory);
assetsSingleContent = assetsBuilder(assets, singleContentChunks, sourceFactory);

assetsBuilder = middlewareInjector(options2, sourceCode, sourceFactory);
assetsMultiContent = assetsBuilder(assets, multipleContentsChunks, sourceFactory)
});

it("Should inject into the background script", () => {
const newBgSource = assetsSingleContent[entriesInfo.background.path].source();
const oldBgSource = assets[entriesInfo.background.path].source();
assert.equal(newBgSource, (sourceCode + oldBgSource));
});

const newBgSource = newAssets[entriesInfo.background.path].source();
const oldBgSource = assets[entriesInfo.background.path].source();
assert.equal(newBgSource, (sourceCode + oldBgSource));
it("Should inject into the a single contentScript", () => {
const newContentSource = assetsSingleContent[entriesInfo.contentScript.path].source();
const oldContentSource = assets[entriesInfo.contentScript.path].source();
assert.equal(newContentSource, (sourceCode + oldContentSource));
});

const newContentSource = newAssets[entriesInfo.contentScript.path].source();
const oldContentSource = assets[entriesInfo.contentScript.path].source();
assert.equal(newContentSource, (sourceCode + oldContentSource));
it("Should inject into the multiple contentScripts", () => {
const newFirstContentSource = assetsMultiContent[entriesInfo.contentScript.path].source();
const oldFirstContentSource = assets[entriesInfo.contentScript.path].source();
assert.equal(newFirstContentSource, (sourceCode + oldFirstContentSource));

const newSecondContentSource = assetsMultiContent[entriesInfo.extraContentScript.path].source();
const oldSecondContentSource = assets[entriesInfo.extraContentScript.path].source();
assert.equal(newSecondContentSource, (sourceCode + oldSecondContentSource));
});
});

it("Should return only changed assets", () => {
const newAssets = assetsBuilder(assets, chunks, sourceFactory);
assetsBuilder = middlewareInjector(options, sourceCode, sourceFactory);
const newAssets = assetsBuilder(assets, singleContentChunks, sourceFactory);

assert.notOk(newAssets.hasOwnProperty(fakeCssPath));
assert.notOk(newAssets.hasOwnProperty(fakeImgPath));
Expand Down
10 changes: 7 additions & 3 deletions src/ChromeExtensionReloader.ts
Expand Up @@ -13,14 +13,18 @@ export default class ChromeExtensionReloader extends AbstractChromePluginReloade

constructor(options?: PluginOptions) {
super();
if(process.env.NODE_ENV !== "production") {
if (process.env.NODE_ENV !== "production") {
const { reloadPage, port, entries } = merge(defaultOptions, options);

const sourceFactory = (...sources): Source => new ConcatSource(...sources);
const sourceFactory = (...sources): Source =>
new ConcatSource(...sources);
const source = middlewareSourceBuilder({ port, reloadPage });

this._injector = middlewareInjector(entries, source, sourceFactory);
this._triggerer = changesTriggerer(new HotReloaderServer(port), reloadPage);
this._triggerer = changesTriggerer(
new HotReloaderServer(port),
reloadPage
);
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/utils/middleware-injector.ts
Expand Up @@ -5,7 +5,11 @@ export default function middlewareInjector(
) {
return (assets: object, chunks: WebpackChunk[]) =>
chunks.reduce((prev, { name, files }) => {
if (name === background || name === contentScript) {
if (
name === background ||
name === contentScript ||
contentScript.includes(name)
) {
const [entryPoint] = files;
if (/\.js$/.test(entryPoint)) {
prev[entryPoint] = sourceFactory(source, assets[entryPoint]);
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"target": "es6",
"target": "es2016",
"sourceMap": true,
"module": "commonjs",
"moduleResolution": "node",
Expand Down
5 changes: 3 additions & 2 deletions typings/index.d.ts
Expand Up @@ -3,7 +3,8 @@ declare type Action = { type: ActionType, payload?: any };
declare type ActionFactory = (payload?: any) => Action;

declare type PluginOptions = { port: number, reloadPage: boolean, entries: EntriesOption };
declare type EntriesOption = { background: string, contentScript: string };
declare type EntriesOption = { background: string, contentScript: ContentScriptOption };
declare type ContentScriptOption = string|Array<string>;

declare type MiddlewareTemplateParams = { port: number, reloadPage: boolean };

Expand All @@ -26,7 +27,7 @@ declare interface Source {
}

declare type SourceFactory = (concatSource: string, rootSource: string) => Source;
declare type WebpackChunk = { files: string[], name: string };
declare type WebpackChunk = { files: Array<string>, name: string };

declare module '*.json' {
const json: any;
Expand Down

0 comments on commit 91f0cf9

Please sign in to comment.