I am trying to make a chrome extension and would love to use React, but after a full week of running into dead ends this seems to not be the ideal route. Thus, I decided to ask for a few suggestions here from you fine SO users as a last ditch effort before simply using Vanilla JS.
Here is my public/manifest.json file (I manually change "js":[] field to match the build folder's name for now):
{
"manifest_version": 2,
"name": "Name",
"version": "0.1.0",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["/static/js/main.356978b8.chunk.js"]
}
],
"background": {
"scripts": ["background.js"]
},
"permissions": ["tabs", "http://*/"],
"browser_action": {
"default_title": "Title"
}
}
My thinking is that the content script would be all the React function component files and the overall index.js using the above.
Here is package.json:
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"env": {
"webextensions": true
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Here is my folder structure:
src folder:
// src/App.js
function App() {
return <div>Hello</div>;
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log(request.tabs);
});
export default App;
// src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
public folder:
// public/background.js
chrome.browserAction.onClicked.addListener((tab) => {
chrome.tabs.create(
{
windowId: null,
url: "http://localhost:3000",
active: true,
openerTabId: tab.id,
},
(newTab) => {
// wait for tab to load, then send message with tabs
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
if (changeInfo.status == "complete" && tabId == newTab.id) {
chrome.tabs.getAllInWindow(null, (tabs) => {
chrome.tabs.sendMessage(newTab.id, { tabs });
});
}
});
}
);
});
// public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Title</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
My background.js simply opens a new tab to http://localhost:3000 when the extension button is clicked with chrome.browserAction.onClicked.addListener((tab)=>{...}). This works fine.
Adding the build folder to Load Unpacked in Chrome Extensions produces the extension, but when I click on the button and the new tab is created, I get
TypeError: Cannot read property 'addListener' of undefined
I know that npm run eject can be used to automate/set the filename in /static/js/..., but for now I am fine with simply changing it on every build change.
Any clues or ideas would be much appreciated!
