awesome-webextension / webpack-target-webextension Goto Github PK
View Code? Open in Web Editor NEWWebExtension Target for Webpack 5. Supports code-splitting and HMR.
License: MIT License
WebExtension Target for Webpack 5. Supports code-splitting and HMR.
License: MIT License
Temporary workaround: Adding a dynamic import in your source code
I should add .e
to the dependency but I don't have the resources to do that. PR welcome.
Chrome has released Manifest V3 in M88, is there any plan to support it?
I have debugged into the webpack and it seems like this library doesn't return a template that web extension can run on.
The dynamic import for MV3 in content scripts using scripting.executeScript does not support injecting into iFrames, since it leaves out the frameIds
parameter.
This is fixed with a PR I just filed.
Workaround: Set output.publicPath
to /
I just tried import(chrome.runtime.getURL())
and it seems to work both in Chrome and Firefox, in content scripts and background pages 🎉
This is because the bug that your readme points to, has been marked as solved and shipped in the latest Firefox release: https://bugzilla.mozilla.org/show_bug.cgi?id=1536094
Could this plugin be modified to use import()
instead of the current messaging mechanism?
Does Webpack even leave native import()
calls in the bundle? In my test, import()
is still converted to Webpack’s own loading mechanism and I don't see anything about raw import()
calls in the docs:
https://webpack.js.org/guides/code-splitting/#dynamic-imports
https://webpack.js.org/api/module-methods/#import-1
I have a file that is being injected in the “unsafe context” via script tag in order to access the data/variables in the live website. This file is being executed outside the extension context and is not a content script.
How do you suggest I exclude it from this plugin? If it included import
calls they’re now broken. Do you think it’s possible to disable the plugin on specific files, either via webpack config or plugin config?
The only option I’m aware of is to have a whole new webpack config/build specific to this file in order to exclude this plugin.
I noticed that this plugin supports HMR, but it's not clear to me what's required to make it work and in what context it can work.
I load my extensions via web-ext run
and they're reloaded (kind of) automatically when my dist
folder changes. This causes the content scripts to lose connection with the background page so they will no longer work until the page is refreshed.
Does HMR only work on chrome-extension://
pages?
Does HMR only work on self-contained React components? i.e. that don't require messaging.
Do I need to set up some "unload" mechanism for the previous content script? Currently a web-ext
reload will just reinject content script and every change to the page will be reapplied (unless I have code to block it)
This library depends on webpack/lib/FunctionModulePlugin but it is removed in Webpack 5
It appears that the plugin for Webpack 5 no longer accessed the nodeConfig. Should it be dropped from this file?
Elements with id
s will automatically become globals so this check is truthy even when it shouldn't be:
Gotta love Netscape.
Test URL: https://ghosttext.fregante.com/
Suggested fix: use this or its globalThis
-less equivalent:
const isModern = typeof globalThis.browser?.runtime?.getURL === 'function'
README says "In content scripts native dynamic import subjects to target page content security policy".
Do you have any sources to back that up?
From a bit of testing it looks to me that it's not true.
For example, here, on GitHub, if you open the console and try typing
import("https://www.example.com/1.js")
it will refuse to do it with reference to the website's CSP, but my extension, which uses import()
, imports its script just fine (it even appears in the Network tab of dev tools).
Also, here are docs for Microsoft Edge extension developers: https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/store-policies/csp#content-scripts, which specifically state the opposite.
Mozilla docs are more general, there is no section for content scripts specifically: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_Security_Policy
I'm opening this issue to collect some information I find.
Appears in the background worker on load:
I think the repro so far is:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [
// This is the order in my config
new WebExtensionTarget({
weakRuntimeCheck: true,
background: {
// it works with `pageEntry` on MV2
serviceWorkerEntry: "background",
}
}),
new MiniCssExtractPlugin(),
],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
import './input.css'
console.log('js loaded')
* {
color: hotpink;
}
First seen in
Thanks for your work on this extension! We're seeing problems with dynamic imports failing for iframes with Webpack 5. I think the problematic line might be:
This should probably be sender.frameId
? The sender.tab.frameId
expression is undefined
.
I am curious as to how this was working previously as that line has been there over a year. Maybe Chrome recently changed what's passed with sender.tab
? In the MDN documentation the tab https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender won't have a frameId
I found an issue with this line:
Which causes an unhelpful error:
ERROR in webpack/runtime/publicPath
outputPath.replace is not a function
because Webpack's getUndoPath
function accepts a string, not a boolean, since v5.26:
Blocked by w3c/ServiceWorker#1356, otherwise, we need to emit import statements which is not easy in a webpack bundle.
I'm still looking into it, but it seems that the first chunk runs correctly, but then anything import()
'd fails with this error:
contentScript.js:18475 Uncaught (in promise) ChunkLoadError: Loading chunk vendors-node_modules_emotion_react_dist_emotion-react_browser_esm_js failed.
(undefined: undefined)
at __webpack_require__.f.j (contentScript.js:18475:29)
at contentScript.js:18249:40
at Array.reduce (<anonymous>)
at __webpack_require__.e (contentScript.js:18248:67)
at initContentScript (contentScript.js:18610:229)
at contentScript.js:18630:10
at contentScript.js:18638:3
at contentScript.js:18640:12
It looks like import
works fine though, I can run this in the console for that frame:
const code = 'export const foo = 123'
const imported = await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(code)}`)
console.log(imported.foo) // Should print 123
I'm trying to ad HMR to my extension but webpack serve
appears to run this code (on css files 🤔) and fail:
ERROR in ./src/vendors/theme/app/app.scss
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
HookWebpackError: No chrome or browser runtime found
at tryRunOrWebpackError (./node_modules/webpack/lib/HookWebpackError.js:88:9)
at __webpack_require_module__ (./node_modules/webpack/lib/Compilation.js:4979:12)
at ./node_modules/webpack/lib/Compilation.js:5003:11
at symbolIterator (./node_modules/neo-async/async.js:3485:9)
at processTicksAndRejections (node:internal/process/task_queues:78:11)
-- inner error --
Error: No chrome or browser runtime found
at Object.get runtime [as runtime] (webpack/runtime/publicPath:3:81)
at webpack/runtime/publicPath:13:37
at ./node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js:460:11
at Hook.eval [as call] (eval at create (./node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:9:1)
at ./node_modules/webpack/lib/Compilation.js:4981:39
at tryRunOrWebpackError (./node_modules/webpack/lib/HookWebpackError.js:83:7)
at __webpack_require_module__ (./node_modules/webpack/lib/Compilation.js:4979:12)
at ./node_modules/webpack/lib/Compilation.js:5003:11
at symbolIterator (./node_modules/neo-async/async.js:3485:9)
at processTicksAndRejections (node:internal/process/task_queues:78:11)
Generated code for webpack/runtime/publicPath
1 | var isBrowser = !!(() => { try { if (typeof browser.runtime.getURL === "function") return true } catch(e) {} })()
2 | var isChrome = !!(() => { try { if (typeof chrome.runtime.getURL === "function") return true } catch(e) {} })()
3 | var runtime = isBrowser ? browser : isChrome ? chrome : { get runtime() { throw new Error("No chrome or browser runtime found") } }
4 | var scriptUrl;
5 | if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
6 | var document = __webpack_require__.g.document;
7 | if (!scriptUrl && document) {
8 | if (document.currentScript)
9 | scriptUrl = document.currentScript.src
10 | }
11 | // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
12 | // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
13 | if (!scriptUrl) scriptUrl = runtime.runtime.getURL("/");
14 | scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
15 | __webpack_require__.p = scriptUrl;
So I wanted to test it out using the examples linked in the readme but everything is missing, all the dependencies and the linked files. Could you include them all to ensure they can be run as-is?
Hello,
I'm trying to fix the dynamic imports in MV3 so I've added new WebExtension
into the plugins array:
new WebExtension({
background: {
entry: `background_gsd.worker`,
// !! Add this to support manifest v3
manifest: 3,
},
}),
And it seems to fix the background service worker, but at the same time my compilation now fails in the HtmlWebpackPlugin
, which doesn't make sense to me:
ERROR in Error: No chrome or browser runtime found
- reload.html:81 Object.get runtime [as runtime]
C:/Users/juraj/git/addons/GroupSpeedDial/src/reload/reload.html:81:92
- reload.html:83
C:/Users/juraj/git/addons/GroupSpeedDial/src/reload/reload.html:83:89
- reload.html:84
C:/Users/juraj/git/addons/GroupSpeedDial/src/reload/reload.html:84:13
- reload.html:118
C:/Users/juraj/git/addons/GroupSpeedDial/src/reload/reload.html:118:12
- node:vm:139 Script.runInContext
node:vm:139:12
- index.js:142 HtmlWebpackPlugin.evaluateCompilationResult
[GroupSpeedDial]/[html-webpack-plugin]/index.js:142:28
- index.js:324
[GroupSpeedDial]/[html-webpack-plugin]/index.js:324:26
- async Promise.all
- async Promise.all
Removing the new WebExtension(...
from the plugins list fixes the problem so there must be some correlation I don't see.
Can you help? Or do I need to provide some example project?
Thanks!
Hi, I'd like to know is it possible for a webpack plugin to re-write the HMR client?
When running HMR in the content script, for an unknown reason it will be blocked by CSP policy.
Refused to connect to 'https://localhost:8080/sockjs-node/info?t=1603432704212' because it violates the following Content Security Policy directive: ......
If it is possible, I'd like to contribute an HMR client that works for the Web Extension.
We have a background script that is started like so.
// ./background.js
import { startApi } from "@sendnodes/pokt-wallet-background"
startApi()
However, this extension fails to add this runtime part in the webpack output. It never fetches all of the modules correctly and so our background script exits without executing the startApi
logic.
/******/ /* webpack/runtime/load script */
/******/ (() => {
/******/ var isBrowser = !!(() => { try { return browser.runtime.getURL("/") } catch(e) {} })()
/******/ var isChrome = !!(() => { try { return chrome.runtime.getURL("/") } catch(e) {} })()
/******/ var runtime = isBrowser ? browser : isChrome ? chrome : (typeof self === 'object' && self.addEventListener) ? { get runtime() { throw new Error("No chrome or browser runtime found") } } : { runtime: { getURL: x => x } }
/******/ var __send__ = (msg) => {
/******/ if (isBrowser) return runtime.runtime.sendMessage(msg)
/******/ return new Promise(r => runtime.runtime.sendMessage(msg, r))
/******/ }
/******/ var classicLoader = (url, done, chunkId) => {
/******/ __send__({ type: 'WTW_INJECT', file: url }).then(done, (e) => done(Object.assign(e, { type: 'missing' })))
/******/ }
/******/ var scriptLoader = (url, done, chunkId) => {
/******/ var script = document.createElement('script')
/******/ script.src = url
/******/ script.onload = done
/******/ script.onerror = done
/******/ document.body.appendChild(script)
/******/ }
/******/ var workerLoader = (url, done, chunkId) => {
/******/ try { importScripts(url); done() } catch (e) { done(e) }
/******/ }
/******/ var isWorker = typeof importScripts === 'function'
/******/ if (location.protocol.includes('-extension:')) __webpack_require__.l = isWorker ? workerLoader : scriptLoader
/******/ else if (!isWorker) __webpack_require__.l = classicLoader
/******/ else { throw new TypeError('Unable to determinate the chunk loader: content script + Worker') }
/******/ })();
The only way to get this plugin to get this part added is to convert our background script is to use dynamic imports. Then, that runtime portion gets added.
setTimeout(async () => {
const { startApi } = await import("@sendnodes/pokt-wallet-background")
startApi()
}, 1);
I don't know enough about webpack compilation steps to be much more help than that, but maybe this has to do with our webpack setup. This is what our plugin looks like.
new WebExtension({
background: {
entry: 'background',
// !! Add this to support manifest v3
manifest: browser == 'chrome' ? 3 : 2,
classicLoader: true
},
weakRuntimeCheck: true,
hmrConfig: false
}),
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.