-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CRXJS doesn't work with content script in "MAIN" world #695
Comments
I encountered this issue also and used a workaround. In manifest.json add the In background.ts register the content script using
|
Having the same issue here as well. Using the workaround @adam-s provided. Works okay, but it doesn't hot-reload the file changes and I have to build + reload the extension to see changes, which is not ideal. Would love to see this fixed. 🙂 |
Just putting it here in case it helps any of you, I found this post written by @jacksteamdev where talks about MAIN world script. But, he mentions injecting script through the ISOLATED content script into the DOM. Which feels like a roundabout way to go about it. Having the dreict Manifest way would be amazing. But at least with that approach, I now get all the dev-tool magic working. |
@faahim Thanks for posting that link. As you said, it is a roundabout way to go about it. Also, that post also seems to assume that all content scripts are in the isolated world, ignoring the option to set All that said, I realized soon after I posted this GitHub issue that all content scripts in the manifest of a CRXJS project automatically get wrapped in the Vite loader code, and since that code relies on Chrome Extension API that is not available in main world, those main-world content scripts simply won't work from a CRXJS project manifest. Perhaps @jacksteamdev could add an option for us to specify which content scripts in the manifest should not be wrapped with Vite loader code. However:
Even if there was a way to make Vite/HMR loader code work in the main world, there's another problem: using the Vite loader introduces a delay to code execution, which prevents those content scripts from running before other scripts on the page. This means that those content scripts will not be effective in overriding built-in functions used by other scripts on the page, and therefore a lot of potential use cases of Chrome extensions are lost by that Vite delay. In the end, I switched to using Webpack and reloading my extension manually. It's a bummer to lose Vite and HMR, but at least I could be sure that my extension scripts will load first and be able to do everything in the expected order. @jacksteamdev I'm interested in discussing this further and trying to find solutions to the above concerns if you have time. |
Correct, @svicalifornia! This is exactly the issue I'm facing with the approach described in the post. The project I'm doing involves wrapping the built-in I love all the other aspects of CRXJS so much that it feels painful to not be able to work with it. I hope @jacksteamdev will take a minute to shed some more light on this. 🙏 |
I found a method of dynamically loading scripts that will execute extension code before the rest of the page is loaded. This should suffice for intercepting fetch API calls, websocket connections and the like.
/* manifest.json */
{
/*...*/
"content_scripts": [
/*...*/
{
"matches": ["myfilter.url.example.com"],
"js": ["path/to/loader_script.js"],
"run_at": "document_start"
}
] @svicalifornia unfortunately this won't solve the issues with vite or HMR. My gut feeling is that running across multiple window scopes may not be something that either module can handle, though I haven't investigated thoroughly. My approach to this so far has been to keep the world scripts as lightweight as possible and emitting events on the |
Hi @gf-skorpach 👋 Thanks for these pointers. While this works, it still isn't ideal. I could be totally wrong here, but the way I understand it, when you add a loader script via the manifest with I think if we're just looking for inserting script in the MAIN world AND guarantee that it runs before the page script, using Ideally, we want to have both, i.e. inserting MAIN world script directly through the manifest and having Vite/HMR for the script. 😅 |
In the end, I would prefer having MAIN world injection with static declaration (manifest.json) and just give up on having Vite/HMR functionality. |
In my personal fork I ended up doing this: if content_scripts entry has This solution is probably not desirable for @crxjs/vite-plugin... |
@adam-s thank you for workaround. Is it possible to still have the script processed by Vite even thought without hot reloading? |
I tried creating a custom plugin as a hack to watch for changes in vite.config.js so that the content script could be compiled from ts to js and moved to the .dist folder. However, I ran into a problem where the plugin runs and compiles after crx runs. The content script injected into the main world needs to be declared in manifest.json web_accessable_resources. Because the file isn't available when crx runs, crx throws an error not knowing the file will be made available later in the compile process. Perhaps have a placeholder file and put the content scripts which you want to compiled and moved into a different folder.
|
Hi, Couldn't we instruct vite for content-scripts with "world main" to bundle all dependencies in one single file without any import/exports? This way, everything else that is needed, such as auto-reload, could also work. |
Here is how I resolved it, I let the content.js remain in ISOLATED world , and then inject my other script in the MAIN world using the manifest.json
The code within ./extractGlobals.js will be executed at document_idle will be available in ISOLATED world from MAIN world where the content script is running at. If you want to extract the background.js or service to grab globals from MAIN land where the content js cannot run, you can use background.js to send a request to inject the file using a Promise like this and you receive the globals in main page, remember you have to specify the globals that are to be returned. // Listen for messages from the content script //background.js // Listen for messages from the content script
|
Bumping this. I would love for a way for the loader script to be able to inject in main without having to do a workaround. Potentially run all the loader script in ISOLATED but detect and inject the script into MAIN when appropriate. |
I have the similar issue.so I try to remove the “world”:”MAIN” to fix it and it works. |
F*ck google's atrocious docs and shoutout this guy crxjs/chrome-extension-tools#695 (comment)
Bless you @emsiakkkk. Was trying to have a content-script use Just dropping “world”:”MAIN” allowed my content-script to once again enjoy |
one way to fix this can be by polyfill the export default defineConfig({
// ... your configs
build: {
rollupOptions: {
plugins: [
{
name: "crx:dynamic-imports-polyfill",
generateBundle(_, bundle) {
const polyfill = `
(function () {
const chrome = window.chrome || {};
chrome.runtime = chrome.runtime || {};
chrome.runtime.getURL = chrome.runtime.getURL || function(path) { return path.replace("assets/", "./"); };
})();
`;
for (const chunk of Object.values(bundle)) {
if (
chunk.name?.endsWith("-loader.js") &&
"source" in chunk &&
typeof chunk.source === "string" &&
chunk.source.includes("chrome.runtime.getURL") &&
!chunk.source.includes(polyfill)
) {
chunk.source = `
${polyfill}
${chunk.source}
`;
}
}
},
},
]
}
}
}); |
Build tool
Vite
Where do you see the problem?
Describe the bug
When my manifest (V3) has a content script with
"world": "MAIN"
, the following error appears in the Chrome console:In the Sources tab, I see that my_content_script.js-loader.js contains:
CRXJS (or Vite?) is trying to call
chrome.runtime.getURL
to get the JS paths to import. However,chrome.runtime
is not defined in the MAIN world.These imports should be rewritten to first check for the existence of
chrome.runtime
— if it doesn't exist, then some sort of shim should be defined forchrome.runtime.getURL
to get the desired paths.As one way to do this, CRXJS could:
document.documentElement
)CRXJS_getRootURL
event to from MAIN to the isolated worldchrome.runtime.getURL('')
and sending it back to MAINchrome.runtime.getURL
, then continuing with the imports as shown in the code block above.Reproduction
mainfest.json:
Logs
System Info
Severity
blocking an upgrade
The text was updated successfully, but these errors were encountered: