crxjs / chrome-extension-tools

Bundling Chrome Extensions can be pretty complex. It doesn't have to be.
https://crxjs.dev/vite-plugin
2.9k stars 192 forks source link

Main world scripts cannot be found in dev mode, nor build #573

Closed NicolasReibnitz closed 1 year ago

NicolasReibnitz commented 1 year ago

Build tool

Vite

Where do you see the problem?

Describe the bug

I'm following the guide here to inject a main-world script. I face errors in both the dev and build modes. (note: I'm by no means a typescript expert! That might be the cause of the problem. Just saying).

Dev mode

The files compile fine, but the main-world script can't be found.

See error log below.

The file dist/src/content-script.ts.js does exist. dist/src/main-world.ts__scriptId--MW0JO.js does not. But there is a dist/src/main-world.ts.js file instead.

Build mode

The compilation fails.

See error log below.

(note: I shortened the paths using [...] myself. That's not the problem. 😄)

Reproduction

I put a simplified test case on GitHub here: https://github.com/NicolasReibnitz/crxjs-test-case

I hope that's okay. Let me know if you need something else.

But these are the main files, I guess:

manifest.config.ts:

import { defineManifest } from '@crxjs/vite-plugin';
import packageJson from './package.json';

const { version } = packageJson;
const [major, minor, patch, label = '0'] = version.replace(/[^\d.-]+/g, '').split(/[.-]/);

export default defineManifest(async env => ({
    manifest_version: 3,
    name: env.mode === 'staging' ? '[INTERNAL] CRXJS Power Tools' : 'CRXJS Power Tools',
    version: `${major}.${minor}.${patch}.${label}`,
    version_name: version,
    content_scripts: [
        {
            js: ['src/content-script.ts'],
            matches: ['*://192.168.10.1/*']
        }
    ]
}));

src/content-script.ts:

import mainWorld from './main-world?script&module';

const appEl = document.createElement('div');
appEl.id = 'app';
document.body.prepend(appEl);

const script = document.createElement('script');
script.src = chrome.runtime.getURL(mainWorld);
script.type = 'module';
document.head.prepend(script);

src/main-world.ts:

import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');

Logs

Errors in the browser:

GET chrome-extension://ikdaaeabkbecfogingbieipafiaijibh/src/main-world.ts__scriptId--MW0JO.js net::ERR_FILE_NOT_FOUND

and

TypeError: Failed to fetch dynamically imported module: chrome-extension://ikdaaeabkbecfogingbieipafiaijibh/src/content-script.ts.js

Error in terminal:

[crx:dynamic-content-scripts-build] Content script refId is undefined: "import.meta.CRX_DYNAMIC_SCRIPT_bb1f701f,t=document.createElement("div");t.id="insta-cheat";document.body.prepend(t);const e=document.createElement("script");e.src=chrome.runtime.getURL(c);e.type="module";document.head.prepend(e);"
error during build:
Error: Content script refId is undefined: "import.meta.CRX_DYNAMIC_SCRIPT_bb1f701f,t=document.createElement("div");t.id="insta-cheat";document.body.prepend(t);const e=document.createElement("script");e.src=chrome.runtime.getURL(c);e.type="module";document.head.prepend(e);"
    at file:///[...]/crxjs-test-case/node_modules/@crxjs/vite-plugin/dist/index.mjs:803:27
    at String.replace (<anonymous>)
    at Object.generateBundle (file:///[...]/crxjs-test-case/node_modules/@crxjs/vite-plugin/dist/index.mjs:798:43)
    at file:///[...]/crxjs-test-case/node_modules/vite/node_modules/rollup/dist/es/shared/rollup.js:22748:40
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

System Info

System:
    OS: macOS 11.7
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 3.71 GB / 32.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 16.14.0 - ~/.nvm/versions/node/v16.14.0/bin/node
    Yarn: 3.2.1 - ~/.nvm/versions/node/v16.14.0/bin/yarn
    npm: 8.9.0 - ~/.nvm/versions/node/v16.14.0/bin/npm
  Browsers:
    Brave Browser: 107.1.45.118
    Chrome: 107.0.5304.87
    Chrome Canary: 109.0.5403.0
    Firefox: 105.0.3
    Firefox Developer Edition: 90.0
    Safari: 16.0
  npmPackages:
    @crxjs/vite-plugin: ^2.0.0-beta.4 => 2.0.0-beta.4 
    vite: ^3.2.0 => 3.2.2

Severity

blocking all usage of RPCE

marcoreni commented 1 year ago

I had the same issue, it seemed to go away in dev mode by setting moduleResolution: node16 in my tsconfig (this has some side effects, for example you need to add .js extensions to all the files. See more here ).

I'm still unable to run build script, getting the same Content script refId is undefined: error. Investigating further...

The serve issue seems to be caused by some misalignment inside the write functions. Files are written without encoding the querystring params (in this case, ?id=...), while the scripts expect to find them there. Probably @jacksteamdev can shed some light on this, having rewritten the writers recently.

For the build see below.

marcoreni commented 1 year ago

@NicolasReibnitz after further investigation, the build issue is caused by a wrong regexp.

It's not catched by tests because the test configuration is:

export default defineConfig({
  build: {
    minify: false,
  },
  [...]
});

in fact, by adding minify: false to your repo the build command worked.

I think the serve and the build issues are different. I will try and submit a PR for the build issue, while the serve is still rather obscure.

jacksteamdev commented 1 year ago

@marcoreni Thanks for the assist and the PR!

jacksteamdev commented 1 year ago

@NicolasReibnitz Thanks for putting together this superb issue! The minimum reproduction is a big help. 💯

There's certainly something fishy going on here. First, the file writer doesn't write the dynamic script file; in this case, it's /src/main-world.ts__scriptId--MW0JO.js, which should be something like export default "/src/main-world.ts.js";.

It seems that Vue in the main world is somehow tripping up the file writer. I refactored your example to use plain TS in the main world, and things seem to work as expected. Once this bug is fixed, other issues will arise related to using Vue in a main world script. Chrome APIs aren't available to main-world content scripts, and CRXJS uses the Chrome API to support HMR.

As a workaround, for now, I would decouple my main-world logic from the Vue app. Then, the Vue app can send messages from the content script to the main world script using window.postMessage or custom DOM events.

NicolasReibnitz commented 1 year ago

Hey guys! Thanks a lot for your quick response! I'll test the new build for the... uhm... build side of things. And I think I get what @jacksteamdev is saying about the HMR issues and I'll see if I can get a postMessage bridge kinda thing working.

Thanks again, I'll let you know how it went and what I did for reference later.

Cheers!

NicolasReibnitz commented 1 year ago

Ah... the new build isn't on NPM yet, is it?

jacksteamdev commented 1 year ago

Sorry about that! I released it to beta earlier today.

NicolasReibnitz commented 1 year ago

Okay, build seems to be working now! Thanks @marcoreni and @jacksteamdev. Dev doesn't. As expected. But to my TypeScript-newbie eyes the scripts (including the main-world) seem pretty TypeScript-y already. What did you change @jacksteamdev?

marcoreni commented 1 year ago

I did some further investigation on this (it happened on one of my projects, that does not use vue but has a main world script with some dependencies).

It seems that if I start the script "a lot of times" files are correctly generated (still unclear why). In a clean scenario (rm -rf node_modules && rm -rf dist && vite dev) it fails every time.

What I'm seeing with some console.log debugging is that some prepScript calls hang here, and that those hangs cause the files to be missing. For example, in my project one of the missing files is injected.ts__scriptId--yqe68.js, and this file is "hanging".

I tried catching exceptions on that transformRequest invocation, but the only error was related to an image, and I don't think that's related (I removed the image import and nothing changed). Listing it here just for reference:

ERROR transformRequest Cannot set properties of undefined (setting 'isSelfAccepting')
TypeError: Cannot set properties of undefined (setting 'isSelfAccepting')
    at TransformContext.transform (file:///Volumes/Workspace/work/git/accedo-chrome-extension/node_modules/.pnpm/vite@3.2.4_@types+node@18.11.9/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:36789:48)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.transform (file:///Volumes/Workspace/work/git/accedo-chrome-extension/node_modules/.pnpm/vite@3.2.4_@types+node@18.11.9/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:40228:30)
    at async loadAndTransform (file:///Volumes/Workspace/work/git/accedo-chrome-extension/node_modules/.pnpm/vite@3.2.4_@types+node@18.11.9/node_modules/vite/dist/node/chunks/dep-67e7f8ab.js:36615:29) {
  plugin: 'vite:import-analysis',
  id: '/Volumes/Workspace/work/git/accedo-chrome-extension/src/fireman-logo.png?import',
  pluginCode: 'export default "/src/fireman-logo.png?import"'
}

I'm trying to see if there are related issues on Vite side but I've just started using it, so maybe this info helps someone else in further investigations.

marcoreni commented 1 year ago

transformRequest for my injected.ts file hangs because one of the dependencies hangs as well. In particular, I have a tippy.js dep, that tries to be resolved as /node_modules/.vite/deps/tippy__js.js?v=2953ce2f. This resolution goes into here where ~and hangs because~ scriptId is handled, but every other scenario is not.

I honestly don't know whether this is correct.

marcoreni commented 1 year ago

After A LOT of try and error I think what's happening here is an infinite loop.

Almost everything works by simply commenting this line. I don't have an exact reason, but my assumption is that:

Therefore, both Vite and the plugin are waiting for each other.

I don't know why the function is there, I don't know what its purpose is or was supposed to be.

By removing it everything works, so...