aklinker1 / vite-plugin-web-extension

Vite plugin for developing Chrome/Web Extensions
https://vite-plugin-web-extension.aklinker1.io/
MIT License
541 stars 47 forks source link

Configure url path for internal extension html page #115

Open jam-fran opened 1 year ago

jam-fran commented 1 year ago

First off, thank you so much @aklinker1 for this plugin -- it's been a lifesaver for me.

I have a question about configuring the url path names for internal extension pages (e.g. onboarding).

I have a page located at src/pages/onboarding/index.html that I would like to be accessible at chrome://<extension-id>/onboarding.

I've added the html path to my vite.config.ts like so:

    plugins: [
      react(),
      webExtension({
        manifest: generateManifest,
        additionalInputs: ['src/pages/onboarding/index.html'],
      }),
    ],

However, this makes the page's url chrome://<extension-id>/src/pages/onboarding/index.html. I'm not sure what other configuration is required in order to achieve what I'm looking for. Any help would be greatly appreciateed.

aklinker1 commented 1 year ago

Short answer: Put the entry point relative to the Vite root where you want it in the final output. There is no other official way.


So right now your project looks like this:

You can't achieve chrome://<extension-id>/onboarding, but you can achieve chrome://<extension-id>/onboarding.html - an extension HTML page is just loaded from your filesystem. Since you need a web server to remove the html extension, that's not possible.

Put your onboarding.html file inside your vite root, which appears to be the project root.

As of right now, there is not an officially supported way to customize the runtime paths of entry points, so moving the source file to a different path relative to the Vite root is the only solution.

Personally, I prefer a very flat directory structure (src/onboarding.html), and I set the Vite root to ./src. That removes the src/ from the output paths, giving me a clearer final URL for my HTML pages.

That said, because the extension ID is random, people aren't going to type your onboarding url in the search box, and the path is less important. It may not be appealing to see chrome-extension://<id>/onboarding/index.html from the developer standpoint, but users will never notice, especially for an onboarding page.

That said, if you really want to customize this, you could open a PR to add support, or you could mess around with the build.rollupOptions, I'll provide an example in a bit....

aklinker1 commented 1 year ago

Alright, I gave it a go, but the closest I came to get it working was to create a new plugin that attempted to modify the output paths, but it only effected the JS file path, not the HTML file path.

    browserExtension({
      manifest: "src/manifest.json",
      browser: process.env.TARGET ?? "chrome",
      htmlViteConfig: {
        plugins: [
          {
            name: "html-rename",
            config(config) {
              const input = config.build?.rollupOptions?.input;
              if (typeof input === "object" && !Array.isArray(input)) {
                input["popup"] = input["src/popup/index"];
                delete input["src/popup/index"];
              }
              return config;
            },
          },
        ],
      },
    })
Default behavior With the custom rename plugin
Screenshot 2023-05-31 at 8 23 59 PM Screenshot 2023-05-31 at 8 22 28 PM

As you can see, the only difference is the JS file.

I'm not really sure how to change the output path of the HTML files, I need to look into that more.

aklinker1 commented 1 year ago

Update: I've figured out how to do this. You just have to move the file after the build is finished. All the CSS and script references are absolute paths, so you don't need to worry about updating the HTML files at all. If you update the rollup bundle, the manifest will also be generated with the correct action.default_popup entry.

Here's a custom plugin that does this:

import fs from 'fs-extra';

export default defineConfig({
  plugins: [
    browserExtension({
      manifest: "src/manifest.json",
      browser: process.env.TARGET ?? "chrome",
      htmlViteConfig: {
        plugins: [
          {
            name: "html-rename",
            async writeBundle(_, bundle) {
              // Update the bundle so the manifest gets generated correctly
              bundle["popup.html"] = {
                ...bundle["src/popup/index.html"];
                fileName: "popup.html"
              }
              delete input["src/popup/index.html"];

              // Actually move the file
              await fs.move("dist/src/popup/index.html", "dist/popup.html");
            },
          },
        ],
      },
    })
  ]
});
aklinker1 commented 1 year ago

If I have time, or someone creates a PR, we could do this automatically for everyone, maybe with an options like this:

export default defineConfig({
  plugins: [
    browserExtension({
      entrypointsPaths: {
        "src/popup/index.html": "popup.html",
        "src/options.html": "options.html",
        "src/overlay.cs.js": "content-scripts/overlay.js",
      }
    })
  ]
});