aklinker1 / vite-plugin-web-extension

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

context method emitFile() is not supported in serve mode. This plugin is likely not vite-compatible. #50

Closed YunFeng0817 closed 1 year ago

YunFeng0817 commented 1 year ago

Thanks for this great Vite plugin. I'm currently using v2.0.0-alpha5 with Vite v3.1.8 It went well for building packages but in the dev mode, there's a warning in the console at the end of the process like this:

image
[plugin:web-extension:manifest] context method emitFile() is not supported in serve mode. This plugin is likely not vite-compatible.

And the browser (web-ext) wasn't started.

Is this a bug or did I configure something wrong?

aklinker1 commented 1 year ago

Nope, dev mode and HMR is the last TODO before v2 can be released, but I haven't had time to work on it lately. Right now V2 just doesn't have any dev mode logic in it.

https://github.com/aklinker1/vite-plugin-web-extension/pull/44

Screen Shot 2022-11-16 at 6 33 05 PM

If you want to use vite dev, you'll need to use the latest stable version, 1.4.4:

https://www.npmjs.com/package/vite-plugin-web-extension

Or use watch mode:

vite build --watch
YunFeng0817 commented 1 year ago

Thanks, I'm using v2 because it supports esm bundling. Is there a way to configue it in v1.4.4?

aklinker1 commented 1 year ago

I'll hide the HMR stuff from the V2 docs for now.

What do you mean by esm bundling? Just set the output format to ESM for everything? Could you share your config, then I can give a recommendation

YunFeng0817 commented 1 year ago

Sure, here's my config for v1

import { defineConfig } from 'vite';
import webExtension, { readJsonFile } from 'vite-plugin-web-extension';
import zip from 'vite-plugin-zip';
import * as path from 'path';
import type { PackageJson } from 'type-fest';
import react from '@vitejs/plugin-react';

export default defineConfig({
  root: 'src',
  // Configure our outputs - nothing special, this is normal vite config
  build: {
    outDir: path.resolve(
      __dirname,
      'dist',
      process.env.TARGET_BROWSER as string,
    ),
    emptyOutDir: true,
  },
  // Add the webExtension plugin
  plugins: [
    react(),
    webExtension({
      // A function to generate manifest file dynamically.
      manifest: () => {
        const packageJson = readJsonFile('package.json') as PackageJson;
        const isProduction = process.env.NODE_ENV === 'production';
        type ManifestBase = {
          common: Record<string, unknown>;
          chrome: Record<string, unknown>;
          firefox: Record<string, unknown>;
        };
        const originalManifest = readJsonFile('./src/manifest.json') as {
          common: Record<string, unknown>;
          v2: ManifestBase;
          v3: ManifestBase;
        };
        const ManifestVersion =
          process.env.TARGET_BROWSER === 'chrome' && isProduction ? 'v3' : 'v2';
        const BrowserName =
          process.env.TARGET_BROWSER === 'chrome' ? 'chrome' : 'firefox';
        const commonManifest = originalManifest.common;
        const manifest = {
          version: packageJson.version,
          author: packageJson.author,
          version_name: packageJson.dependencies?.rrweb?.replace('^', ''),
          ...commonManifest,
        };
        Object.assign(
          manifest,
          originalManifest[ManifestVersion].common,
          originalManifest[ManifestVersion][BrowserName],
        );
        return manifest;
      },
      assets: 'assets',
      browser: process.env.TARGET_BROWSER,
      webExtConfig: {
        startUrl: ['github.com/rrweb-io/rrweb'],
        watchIgnored: ['*.md', '*.log'],
      },
      additionalInputs: ['pages/index.html', 'content/inject.ts'],
    }),
    process.env.ZIP === 'true' &&
      zip({
        dir: 'dist',
        outputName: process.env.TARGET_BROWSER,
      }),
  ],
});

The output files are in UMD format. I want to inject an output js file into web pages. But UMD format causes a problem on some pages like Facebook. The page has a predefined AMD system and causes the execution of my output js to fail.

image
aklinker1 commented 1 year ago

I see, so you want the inject.ts file to be built in ESM so you can inject it onto the page via a script element or something like that? So that file runs on in the page's context, not in the content-script context.

I've done this in the past by using a separate build command, but if you want to do it all in a single build step... You could write a custom plugin that changes the format to ESM if the input's name is injector.ts. Since TS files in the additionalInputs are bundled individually, something like this will adjust the format of that one file:

import { defineConfig, PluginOption } from "vite";
import webExtension from "vite-plugin-web-extension";
import path from "node:path";

function useEsmFormat(entriesToUseEsm: string[]): PluginOption {
  return {
    name: "use-esm-format",
    config(config) {
      const shouldUseEsm = entriesToUseEsm.includes(config.build?.lib?.entry);
      if (shouldUseEsm) {
        config.build ??= {};
        // @ts-expect-error: lib needs to be an object, forcing it.
        config.build.lib ||= {};
        // @ts-expect-error: lib is an object
        config.build.lib.formats = ["es"];
      }
    },
  };
}

export default defineConfig({
  root: "src",
  build: {
    outDir: "dist",
  },
  plugins: [
    webExtension({
      manifest: "src/manifest.json",
      assets: "assets",
      additionalInputs: ["injected.ts"],
    }),
    useEsmFormat([path.resolve(__dirname, "injected.ts")]),
  ],
});
YunFeng0817 commented 1 year ago

@aklinker1 Exactly, thanks for your suggestion and it works well.