JohnBra/vite-web-extension

Cannot use import in content script

G4den opened this issue ยท 15 comments

G4den commented

Im getting "Uncaught SyntaxError: Cannot use import statement outside a module"
When trying to use import within a content script.

https://stackoverflow.com/questions/48104433/how-to-import-es6-modules-in-content-script-for-chrome-extension

This stackoverflow post here explains it can get solved by wrapping it with <script type="module">.
But im a bit of a noobie with vite so i can't figure out how to do it.

Maybe its also a solution to use vite to transpile the content script?

G4den commented

At the bottom of the stackoverflow thread, someone wrote "For Vite Users"
And having a solution with something called crxjs
Only problem with that, is that it requires your to import you manifest.json file
And this template doesnt have that. Only manifest.ts

G4den commented

I found out the issue happens, when the same imports is being used in both content script, and popup
Here is a reproduction repo
https://github.com/G4den/vite-web-extension

Only things changed is i've installed the package ethers, and used it in both content and popup

Hey @G4den, thanks for opening the issue.

I'm currently incredibly busy and will see if I find the time to get to it.

If you find a solution to the problem in the meantime, feel free to open a PR.

If all fails, you could use require() for now

@G4den as you mentioned, crxjs does work. It's pretty fast to integrate (just copy manifest.ts into a new manifest.json) and as a plus you can uninstall nodemon and rollup. Maybe this boilerplate should be convert to crxjs.

Hey @williamyeny I have actually looked into crxjs maybe half a year ago, but it didn't do what I wanted it to do back then.

If you can create a working PR, I'd be happy to merge it into the template ๐Ÿ™‚

If all fails, you could use require() for now

Using require() in content/index.ts gives Uncaught ReferenceError: require is not defined for me

Also facing this. It is a general issue with the content script - https://stackoverflow.com/questions/48104433/how-to-import-es6-modules-in-content-script-for-chrome-extension.

I ended up having a separate vite config file for the content script:

import { resolve } from "path";
import { defineConfig } from "vite";
import { viteSingleFile } from "vite-plugin-singlefile";

const root = resolve(__dirname, "src");
const pagesDir = resolve(root, "pages");
const outDir = resolve(__dirname, "dist", "src", "pages", "content");

const IS_DEV = process.env.DEV === "true";

export default defineConfig({
  resolve: {
    alias: {
      "@src": root,
      "@pages": pagesDir,
    },
  },
  plugins: [viteSingleFile()],
  build: {
    copyPublicDir: false,
    minify: !IS_DEV,
    outDir,
    sourcemap: IS_DEV,
    emptyOutDir: IS_DEV,
    rollupOptions: {
      input: {
        content: resolve(pagesDir, "content", "index.ts"),
      },
      output: {
        entryFileNames: "index.js",
      },
    },
  },
});

And, I changed the scripts in the package.json to:

  "scripts": {
    "build:general": "vite build",
    "build:content": "vite build --config vite.contentScript-config.ts",
    "build": "run-p build:*",
    "dev:general": "vite build --watch --mode development",
    "dev:content": "vite build --config vite.contentScript-config.ts --watch --mode development",
    "dev": "run-p dev:*"
  }

Don't forget to install npm-run-all and vite-plugin-singlefile, if you want to give it a go

Hey guys,

I'm still incredibly busy and don't really have time to implement the crxjs solution mentioned by @G4den

If anyone wants to do this, feel free. I will make sure to merge it ASAP

We can't even define React components in the content script - looks fairly useless as it is. :/

Are there any proper solutions for this?

@mr0b0t0 how do you now define a React component in a different file and import it? It doesn't work when I try. The compiler complains about React syntax.

In my case commonjsHelper.js chunk was created for content script
I have referred -> rollup/rollup#2756 (comment)

and have created a custom plugin

root/utils/plugins/build-content.ts

import colorLog from '../log';
import { PluginOption, build } from 'vite';
import { fileURLToPath } from 'url';

const packages = [
  {
    content: fileURLToPath(
      new URL('../../src/pages/content/index.ts', import.meta.url)
    ),
  },
];

export default function buildContent(): PluginOption {
  return {
    name: 'build-content',
    async buildEnd() {

      for (const _package of packages) {
        await build({
          publicDir: false,
          build: {
            outDir: 'build',
            sourcemap: true,
            emptyOutDir: false,
            rollupOptions: {
              input: _package,
              output: {
                entryFileNames: (chunk) => {
                  return `src/pages/${chunk.name}/index.js`;
                },
              },
            },
          },
          configFile: false,
        });
      }
      colorLog('content file build', 'success');
    },
  };
}

after this used plugin in vite.config.ts
removed content: resolve(pagesDir, 'content', 'index.ts') from vite.config.ts
this worked for me and all code for content script was bundled in single file

@gordicaleksa with this, i also injected react component in content script which worked
make sure if you are using jsx in file, name the file extension as tsx or jsx

I don't know if this functionality is scalable so have not created PR

In my case commonjsHelper.js chunk was created for content script I have referred -> rollup/rollup#2756 (comment)

and have created a custom plugin

root/utils/plugins/build-content.ts

import colorLog from '../log';
import { PluginOption, build } from 'vite';
import { fileURLToPath } from 'url';

const packages = [
  {
    content: fileURLToPath(
      new URL('../../src/pages/content/index.ts', import.meta.url)
    ),
  },
];

export default function buildContent(): PluginOption {
  return {
    name: 'build-content',
    async buildEnd() {

      for (const _package of packages) {
        await build({
          publicDir: false,
          build: {
            outDir: 'build',
            sourcemap: true,
            emptyOutDir: false,
            rollupOptions: {
              input: _package,
              output: {
                entryFileNames: (chunk) => {
                  return `src/pages/${chunk.name}/index.js`;
                },
              },
            },
          },
          configFile: false,
        });
      }
      colorLog('content file build', 'success');
    },
  };
}

after this used plugin in vite.config.ts removed content: resolve(pagesDir, 'content', 'index.ts') from vite.config.ts this worked for me and all code for content script was bundled in single file

@gordicaleksa with this, i also injected react component in content script which worked make sure if you are using jsx in file, name the file extension as tsx or jsx

I don't know if this functionality is scalable so have not created PR

This looks really helpful @mehimanshupatil!

Would you mind creating a pull request to add this to the repo? ๐Ÿ™‚

@G4den
can you test PR #10

@mehimanshupatil I will test it in the coming days if G4den doesn't reply.

We now have a bigger issue: building an extension with tailwind seems to interfere with the CSS of pages where we embed the extension (in our case YouTube).

How does one go about it?

People are mentioning iframe as a solution, but it looks very limiting. Also one can disable the tailwind base layer (corePlugins: { preflight: false }) but that beats the purpose of using tailwind in the first place.

I wonder whether someone has built an actual fully functional extension using this stack so far?

Tailwind units are based on the rem unit, where 1rem is equal to 16px. However, there are cases, such as YouTube, where they have set it to 10px, which can cause compatibility issues.

one solution might be to convert rem units to px units in your CSS code, there are postcss plugins which automatically converts rem units to px units during the build process.

check this plugin: https://github.com/TheDutchCoder/postcss-rem-to-px