vite-plugin-chrome-extension

repository·main·Indexed 19 days ago

https://github.com/starkshang/vite-plugin-chrome-extension

A Vite plugin for building Chrome extensions, evolved from rollup-plugin-chrome-extension to support Vite and Chrome Extension Manifest V3. It automates the handling of manifest.json and HTML inputs, provides a simpleReloader for development auto-reloading, and includes a browserPolyfill to enable the standardized WebExtension API (browser namespace) for cross-browser compatibility.

Tokens
6.7K
Snippets
27
Records
33
Agent score
67%

What's inside vite-plugin-chrome-extension

  1. Project structure for vite-plugin-chrome-extension templates

    main

    When using the project template, the directory structure is organized to separate extension components (like popups, options, and content scripts) from static assets and configuration.

    • public/: Contains static files that are copied directly to the build output, including _locales/ for internationalization, icons/ for extension icons, and browser-extension.html (the default target HTML template).
    • src/assets/: Contains static assets used within your application code (e.g., logos).
    • src/content-scripts/: Contains logic for content scripts that run in the context of web pages.
    • src/popup/, src/options/, src/devtools/, src/standalone/, src/override/: These directories house the specific UI components for different extension parts (Popup, Options page, DevTools, etc.), typically using a framework like Vue or Svelte.
    • background.js: The service worker or background script for the extension.
    • manifest.json: The core Chrome extension manifest file.
  2. TypeScript configuration: Using global.d.ts for type information

    main
    This template uses a global.d.ts file with triple-slash references instead of setting compilerOptions.types in tsconfig.json. This approach allows the project to add svelte and vite/client type information while maintaining the default TypeScript behavior of accepting type information from the entire workspace. Setting compilerOptions.types would explicitly shut out all other types not listed in the configuration.
  3. Preserve component state during HMR using external stores

    main

    HMR (Hot Module Replacement) state preservation is disabled by default in svelte-hmr and @sveltejs/vite-plugin-svelte due to unpredictable behavior. If you have component state that must be retained during development when files change, move that state into an external Svelte store. External stores are not replaced by HMR, allowing the state to persist.

    // store.ts
    // An extremely simple external store
    import { writable } from 'svelte/store'
    export default writable(0)
  4. Configure `dynamicImportWrapper` for event handling

    main

    Because the plugin uses dynamic imports to support code-splitting, background script events might be lost if they trigger before modules are fully loaded. Use dynamicImportWrapper to manage this.

    • wakeEvents (string[]): A list of events (e.g., 'chrome.runtime.onMessage') that the script loader will defer until all background modules have finished loading.
    • eventDelay (number | boolean): A delay in milliseconds to wait after all modules have loaded before triggering wake events. Useful for asynchronous event listeners.
    // Example: deferring specific events
    chromeExtension({
      dynamicImportWrapper: {
        wakeEvents: ['chrome.contextMenus.onClicked'],
        eventDelay: 50,
      },
    })
  5. Exclude unwanted permissions in `manifest.json`

    main

    If a third-party module references a Chrome API that you do not want to include in your final manifest, prefix the permission with an exclamation mark (!) in your source manifest.json. The plugin will then exclude it from the output.

    // source manifest.json
    {
      "permissions": [
        "!alarms", // This permission will be excluded
        "storage"
      ]
    }
  6. Configure vite-plugin-chrome-extension in vite.config.ts

    main

    To use the plugin, import chromeExtension from vite-plugin-chrome-extension and add it to the plugins array in your vite.config.ts.

    Crucially, you must set the build.rollupOptions.input to point to your extension's manifest.json file so that the plugin can correctly process the extension structure.

    // vite.config.ts
    import { resolve } from "path";
    import { defineConfig } from "vite";
    import { chromeExtension } from "vite-plugin-chrome-extension";
    
    export default defineConfig({
        resolve: {
            alias: {
                "@": resolve(__dirname, "src"),
            },
        },
        build: {
            rollupOptions: {
                input: "src/manifest.json"
            }
        },
        plugins: [
            chromeExtension()
        ],
    })
  7. How the browser-polyfill plugin works

    main

    The browser-polyfill plugin automates the injection of the webextension-polyfill library into your extension's entry points to ensure cross-browser compatibility.

    Injection Mechanism

    1. Asset Generation: It resolves the webextension-polyfill package, processes its source maps, and emits assets/browser-polyfill.js into your build bundle.
    2. Manifest Modification: It intercepts the manifest.json during the generateBundle phase and performs the following:
      • Background Scripts: Updates manifest.background.service_worker to point to the polyfill asset.
      • Content Scripts: Prepends the polyfill path to the beginning of the js array in each content_scripts entry.
    3. ExecuteScript Helper: If executeScript: true is configured, it generates an additional asset assets/browser-polyfill-executeScript.js and configures the background service worker to use this helper, which facilitates script execution tasks.

    Requirements

    • The plugin expects a manifest.json to be present in the bundle output.
    • If using the firefox-addon plugin, the polyfill is only added if crossBrowser is not explicitly disabled in the chrome-extension plugin configuration.
  8. Configure `chromeExtension` options

    main

    The chromeExtension function accepts an optional configuration object. Below are the available properties:

    • browserPolyfill (boolean): Adds the Mozilla promisified Browser API to your extension. If using TypeScript, install @types/firefox-webext-browser for Intellisense.
    • dynamicImportWrapper (object | false): Configures how dynamic imports are handled to support ES modules and code-splitting in background/content scripts. Setting this to false disables code-splitting.
    • verbose (boolean): Controls whether the "Detected permissions" message is shown. Defaults to true.
    • pkg (object): Used to manually provide name, description, and version from a package.json if not running via npm scripts.
    • publicKey (string): Sets the manifest.key to help stabilize the extension ID during development.
  9. Extend the Chrome Extension Manifest

    main

    The extendManifest option in ChromeExtensionOptions provides two ways to modify your extension's manifest.json during the build process:

    1. Partial Object: Provide a Partial<ChromeExtensionManifest> to merge new keys or override existing ones.
    2. Transformation Function: Provide a function that receives the current manifest as an argument and returns a new, modified ChromeExtensionManifest object.

    This is useful for dynamically injecting permissions, action settings, or version numbers based on environment variables or package metadata.

    // Using a function to extend the manifest
    extendManifest: (manifest) => ({
      ...manifest,
      permissions: [...manifest.permissions, 'storage', 'tabs'],
      host_permissions: ['https://*.google.com/*']
    })
  10. Expose assets via web_accessible_resources

    main

    To allow external websites to access assets (like images, scripts, or styles) stored within your Chrome extension, you must define them in the web_accessible_resources object in your manifest.json. This object maps specific assets to the target domains allowed to access them.

    Access Methods

    1. Static Referencing: A website can access an asset by using the direct extension URL format: chrome-extension://<extension-id>/<image-path>

    2. Dynamic Injection: You can inject assets into a page at runtime by using the chrome.runtime.getURL() API to resolve the correct extension URL for a specific file.

    Example Configuration Logic

    In this demo, assets are restricted so that:

    • web-accessible-resources-1.glitch.me can only access test1.png and test2.png.
    • web-accessible-resources-2.glitch.me can only access test3.png and test4.png.