'Getting "Uncaught ReferenceError: i is not defined" error whenever I want to use an exported element from another file
I'm developing a chrome extension using webpack and babel.
I have this popup.js:
import 'regenerator-runtime/runtime' // This is required in every JS files that has an async-await function!!
const { NEW_MAPPING_BUTTON } = require('./constants/mapping-page-elements')
const gen_traffic = document.querySelector('.gen-traffic')
gen_traffic.addEventListener('click', async (e) => {
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: getPageData,
});
})
const getPageData = () => {
console.log('okk?')
console.log( NEW_MAPPING_BUTTON )
}
mapping-page-elements.js:
exports.NEW_MAPPING_BUTTON = () => {
return document.querySelector("app-mappings")
}
webpack.common.js:
const path = require("path");
const ESLintPlugin = require("eslint-webpack-plugin");
module.exports = {
entry: ["regenerator-runtime/runtime.js", "./src/popup.js"],
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
],
noParse: /(mapbox-gl)\.js$/
},
resolve: {
extensions: ["*", ".js"],
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
devServer: {
static: path.resolve(__dirname, "./dist"),
},
plugins: [new ESLintPlugin()],
};
For some reason, whenever I use the "NEW_MAPPING_BUTTON" in the popup.js file, it gives me "i is not defined" error. I tested the same element that i exported locally in popup.js and it was returning the element just fine, so I believe this is a problem with webpack's exporting system. Thanks in advance.
Solution 1:[1]
Since the function is executed in the context of the page, its original context is lost as explained the Chrome extension docs:
This function will be executed in the context of injection target. However, this will not carry over any of the current execution context of the function. As such, bound parameters (including the this object) and externally-referenced variables will result in errors.
For a start, referencing variables from outside the definition of the function will fail, including imports.
As Webpack (and or Babel/Typescript) bundle and transpile your file, some language features are replaced by polyfills. In my case an error instanceof Error
check was transpiled to _instanceof(error,Error)
or something along those lines, where _instanceof
is a custom function defined in the file. Consequently, this custom functions will not be available when the function is eventually executed. This exacerbates the problem described above.
Possible solutions
Create new file
Create a new file instead with the contents of the function, with a matching webpack entrypoint/bundle. You can then use the files
argument of chrome.scripting.executeScript
:
const tabs = await chrome.tabs.query({ active: true, currentWindow: true })
const tabId = tabs[0].id
chrome.scripting.executeScript(
{
target: {tabId: tabId},
files: ['getPageData.js'],
}
Pros:
- You can import values into the new file "normally" and it will get bundled properly
Cons:
- Getting a value back from the file is brittle. According to the docs, the last expression in the file is returned. However, the same "bundling problem" described above means you cannot ascertain what the last line in the file will be.
Inject a script
You can inject a script in other ways besides chrome.scripting
as explained here. The answer also has some links elaborating how you can set up two-way communication with the injected script.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Neville Omangi |