Neutrino 9 preset for WebExtension development with hot reload and framework devtools.
- Zero upfront configuration necessary to start developing and building a WebExtension.
- Real extension live reloading.
- Or run development mode in a fake WebExtension environment which supports hot reload and framework devtools.
- Webpack chunks are translated into manifest configs nicely.
- Supports code-splitting with native dynamic import(Caveats).
- Outputs are automatically bundled for each browser respectively, with different manifests.
- Works well with other official Neutrino presets.
Neutrino combines the power of webpack with the simplicity of presets. It is the best scaffolding (AFAIK) on balancing Simplicity and Flexibility.
Saladict |
---|
Five-star extension with 300k+ users |
- Node.js ^8.10 or 10+
- Yarn v1.2.1+, or npm v5.4+
- Neutrino 9
- webpack 4
- webpack-cli 3
- webpack-dev-server 3
- sinon-chrome >=2 (for fake environment and testing)
- webextension-polyfill latest (optional, for Chrome)
Yarn
❯ yarn add --dev neutrino-webextension sinon-chrome webextension-polyfill
Npm
❯ npm install --save-dev neutrino-webextension sinon-chrome webextension-polyfill
webextension-polyfill
is optional.
This preset follows the standard project layout specified by Neutrino. This means that by default all project source code should live in a directory named src
in the root of the project. This includes JavaScript files, CSS stylesheets, images, and any other assets that would be available to your compiled project.
src/manifest/
is the default manifest directory. See explanations below.
src/manifest/
├── chrome.manifest.json
├── common.manifest.json
├── firefox.manifest.json
└── [other browser].manifest.json
This preset is designed to work with eslint preset and web preset or other Neutrino presets that are based on eslint or web (e.g. standardjs or react).
This preset should be placed after all other presets. Example:
// .neutrinorc.js
const react = require('@neutrinojs/react')
const webext = require('neutrino-webextension')
module.exports = {
options: {
mains: {
background: {
entry: 'background',
webext: {
type: 'background'
}
},
popup: {
entry: 'popup',
webext: {
type: 'browser_action',
manifest: {
browser_style: false,
default_title: 'My Popup Page'
}
}
},
content1: {
entry: 'content1',
webext: {
type: 'content_scripts',
manifest: {
matches: ['<all_urls>']
}
}
},
content2: {
entry: 'content2',
webext: {
type: 'content_scripts',
manifest: {
matches: ['https://github.com/crimx/neutrino-webextension'],
run_at: 'document_start',
match_about_blank: true,
all_frames: true
}
}
}
}
},
use: [
react(),
webext({
polyfill: true
})
]
}
This repo itself is also a workable example (But for demo only. Do not use directly in production).
git clone git@github.com:crimx/neutrino-webextension.git
cd neutrino-webextension
yarn install
yarn start --open-page popup.html
# or
yarn build
Follow instructions of other presets. Typically:
yarn start
Or to jump to a specific entry:
yarn start --open-page [entry name].html
Or just keep one entry for faster bundling:
yarn start --wextentry [entry name]
yarn build --debug
yarn build --livereload
With webpack watch
yarn build --livereload --debug --watch
Follow instructions of other presets.
This preset should be the last one so that it can duplicate outputs for different browsers.
You can either pack zip files and manually upload via websites or use CLI tools.
Add "zip": yarn neutrino-webextension-zip"
to package.json scripts.
yarn zip
Zip files for project source and each browser are generated at Neutrino output
directory.
Source zip respects .gitignore
by default. If you want to add or remove files, pass any number of glob patterns as arguments. Negative patterns must come first, then normal patterns. For example:
yarn neutrino-webextension-zip '!test/specs/**/*' '.env'
See wext-shipit which is based on web-ext and chrome-webstore-upload-cli.
Use sinon-chrome
which supports Chrome and Firefox API stubs.
All options are optional.
polyfill
: boolean or string. Defaultfalse
. Generate polyfill related configs. Iftrue
thewebextension-polyfill
should be installed. You can also provide path to a custom polyfill file. If you already importwebextension-polyfill
in your code, you can set this option to false.removePolyfillSourcemap
: boolean. Defaulttrue
on production. Remove link to source map inwebextension-polyfill
.manifest
: string. Default'<neutrino.options.root>/src/manifest/'
. Extension manifests directory.- This directory should have at least a
[browser].manifest.(json|js)
(e.g.firefox.manifest.json
). This preset will read the manifest directory to get browser names and generate outputs respectively. - A
common.manifest.(json|js)
can be added for shared values. - Version number is copied from package.json by default. If you specify
version
field on any manifest.json it will overwrite the default. This is not recommended. You should only perform sematic updates on package.json to avoid confusion. See standard-version for example.
- This directory should have at least a
template
: string. Default<neutrino-webextension_root>/template.ejs
. Path to a special template for html-webpack-plugin. You normally don't have to change this. The html-webpack-plugin can be configured through web preset or other Neutrino presets that are based on web (e.g react). If you really need to replace the template, copy template.ejs and make your own.setup
: string. Default empty. Path to a setup file for development which will run right after the fake WebExtension environment and before running other scripts including webextension-polyfill. Relative path is resolved relative to Neutrinoroot
path. Here you can add other polyfills or play with the sinon-chrome stubs. If you think some of the fake values or functionalities should be added by default, please open a PR towebextensions-emulator
.
WebExtension manifest options page
, content_scripts
, background
, pageless
, browser_action
, page_action
, options_page
and options_ui
SHOULD only be configured in .neutrinorc.js
. This preset will handle file dependencies for you. If you manually add these fileds in common.manifest.json
or [browser].manifest.json
, options may get overwritten.
Specificity: .neutrinorc.js
< common.manifest.json
< [browser].manifest.json
Options are merged using default deepmerge strategy.
Entry options can be configured through options.mains.[entry].webext
. All are optional.
webext.type
:'page'
|'pageless'
|'content_scripts'
|'background'
|'browser_action'
|'page_action'
|'options_page'
|'options_ui'
. Default'page'
.'page'
: Generate normal output. No manifest configuration.'pageless'
: Generate normal output without html. No manifest configuration.- Others: Generate normal output with or without html. Configure manifest according to the docs.
- Except
'page'
,'pageless'
and'content_scripts'
other entry types should be one and only. 'browser_action'
and'page_action'
are merged in to'action'
for manifest v3.
webext.manifest
: object. Other manifest options for this field. See example above.webext.setup
: string. Default empty. Path to a setup file which will run right before this entry on development mode. Relative path is resolved relative to Neutrinosource
path.
By default neutrino will use your preset in .neutrinorc.js
. Then in your webpack.config.js
you can simply call neutrino preset this way:
module.exports = require('neutrino')().webpack()
Webpack is able to launch multiple builds (for instance if you have two extenions or are building manifest v2 and v3) by exporting an array of builds. Also, you can specify to neutrino the preset object you want to use.
const neutrino = require('neutrino');
const presets1 = require('./neutrino-conf-1.js');
const presets2 = require('./neutrino-conf-2.js');
const build1 = neutrino(presets1).webpack();
const build2 = neutrino(presets2).webpack();
module.exports = [
build1,
build2
];
Google is pushing the use of Manifest v3 that brings lot of new changes to extensions. Hopefully, neutrino-webextension
is compatible.
In this documentation you will be guided through the differences between v2 and v3. We also recommend you to take a look at the migration checklist to make sure you didn't miss any change impacting your extension.
As Firefox is not handling manifest v3 as of today, take a look at the paragraph "Handling multiple presets" to see how to build both manifest version at the same time.
yarn build --debug
- Adds
process.env.DEBUG
variable withtrue
. - Adds source map.
- Removes compression.
yarn build --livereload
# specify browser application
yarn build --livereload=firefox
# with arguments
yarn build --livereload=google-chrome --livereloadargs='--profile-directory=Profile 1'
Behind the scene whenever webpack finishes bundling, it opens a special url in the browser running the extension. The url is then captured by the injected script which notifies the extension to reload itself.
In order to redirect web requests and reload itself, additional files and permissions are added to the extension. Do not run --livereload
in production.
These are added to the manifest:
- Web requests related permissions.
- Browsing data permission (for removing special page history).
- Livereload assets are added to web accessible resources.
- Background page becomes persistent.
Live reloading is not compatible with Manifest v3.
If you don't use dynamic import skip this section.
Native dynamic import is buggy in Firefox. A workaround is to write a postbuild script targeting only Firefox build. It should collect all the dynamic chunks and append them to every entries in htmls and the manifest.json
script lists.
See real-life example.