aklinker1 / vite-plugin-web-extension

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

[Question] HMR for Content Scripts - import.meta.hot undefined #197

Closed ohager closed 4 months ago

ohager commented 4 months ago

NOT A BUG - BUT A QUESTION

Summary

I'm trying to make HMR work for a content script. I do know that HMR is somewhat limited for content script, but an auto-reload would be satisifiable already. Unfortunately, I'm not able to make it work.

My current vite.config.js looks like this:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import webExtension, { readJsonFile } from 'vite-plugin-web-extension';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import path from 'path';
import dotenv from 'dotenv';

dotenv.config();

function getEnvVars() {
  const env = Object
    .entries(process.env)
    .filter(([key]) => key.startsWith('REACT_APP_'))
    .reduce((envVars, [key, value]) => {
      envVars[`process.env.${key}`] = JSON.stringify(value);
      return envVars;
    }, {
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    });

  console.info("Injecting Env Vars:", Object.keys(env).join("\n"))
  return env;
}

// acceptable TARGET: chrome, edge, firefox, opera
const browser = process.env.TARGET || 'chrome';

function generateManifest() {
  const manifest = readJsonFile(`src/baseManifest_${browser}.json`);
  const pkg = readJsonFile('package.json');

  console.info(`Generating Manifest for ${browser}`);
  console.info(`Name: ${pkg.name}`);
  console.info(`Version: ${pkg.version}`);

  return {
    name: pkg.name,
    description: pkg.description,
    version: pkg.version,
    ...manifest,
  };
}

// https://vitejs.dev/config/
export default defineConfig({
  define: {
    // see vite-env.d.ts
    __APP_INFO__: { browser},
    ...getEnvVars(),
  },
  resolve: {
    alias: {
      modules: path.resolve(__dirname, 'src/modules'),
    },
  },
  plugins: [
    react(),
    webExtension({
      browser,
      manifest: generateManifest,
    }),
    viteStaticCopy({
      targets: [
        {
          src: 'src/assets/*',
          dest: 'assets',
        },
        {
          src: 'src/translations/*',
          dest: '_locales',
        },
      ],
    }),
  ],
});

and I use the following snippet in my content script:

import browser from 'webextension-polyfill';

(()=> {
  if (process.env.NODE_ENV === 'development') {
    console.log("********************************************************\n", "                   DEVELOPMENT MODE                     \n","********************************************************")

    console.log("import.meta", import.meta.hot)// <<<< this is always undefined 

    // HMR setup
    import.meta.hot && import.meta.hot.accept(() => {
      browser.runtime.sendMessage("@@RELOAD_CONTENT_SCRIPT").then(() => {
        window.location.reload(); // Reload the content script
      })
    });
  }

})()

But import.meta.hot is always undefined.

Environment

  System:
    OS: Linux 5.15 Ubuntu 20.04.6 LTS (Focal Fossa)
    CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
    Memory: 21.20 GB / 31.19 GB
    Container: Yes
    Shell: 5.0.17 - /bin/bash
  Binaries:
    Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node
    Yarn: 1.22.21 - ~/.nvm/versions/node/v18.19.0/bin/yarn
    npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm
    Watchman: 20210523.214641.0 - /usr/local/bin/watchman
  Browsers:
    Brave Browser: 124.1.65.132
    Chrome: 124.0.6367.207

And a follow up question: The auto open browser function always starts a "clean" browser instance. When using another browser instance the extension does not update automatically. How can I make auto-update work with my "own" browser instance.

aklinker1 commented 4 months ago

Content scripts are always built using lib mode, so they never have access to import.meta.hot.

The auto open browser function always starts a "clean" browser instance. When using another browser instance the extension does not update automatically. How can I make auto-update work with my "own" browser instance

https://vite-plugin-web-extension.aklinker1.io/guide/configure-browser-startup.html#available-options

This is something I found out recently that's not documented, but you can use chrome's --user-data-dir and it will create a profile that remembers the website you're logged into for one project. You'll have to spend a couple minutes setting up that profile once you run it for the first time, but after that it will remember everything like a normal browser profile.

# ./.webextrc.yml
args:
  - --user-data-dir=./chrome-data
ohager commented 4 months ago

Cool, the auto-extension update works nicely when using the auto-launched instance - and using the custom chrome data location improves the DX even more. As for completion here is a list with chrome runner options: https://gist.github.com/dodying/34ea4760a699b47825a766051f47d43b

For those who wants to run in maximized mode, the following config works pretty nice (json5 format used here):

{
  // look here for more options:
  // https://vite-plugin-web-extension.aklinker1.io/guide/configure-browser-startup.html#available-options
  // furthermore:
  // chrome: https://gist.github.com/dodying/34ea4760a699b47825a766051f47d43b
  "args": [
    "--user-data-dir=./.chrome-data",
    "--start-maximized=true"
  ]
}
ohager commented 4 months ago

@aklinker1 do you see a way to auto-reload the content script?

ohager commented 4 months ago

@aklinker1 do you see a way to auto-reload the content script?

https://vite-plugin-web-extension.aklinker1.io/guide/development.html#development-mode -- tried this, but the page is not being reloaded

ohager commented 4 months ago

will close for, as all answered