Giter Site home page Giter Site logo

treora / vite-plugin-web-extension Goto Github PK

View Code? Open in Web Editor NEW

This project forked from aklinker1/vite-plugin-web-extension

0.0 0.0 0.0 270 KB

Vite plugin for bundling Chrome/Browser Extensions

License: MIT License

JavaScript 0.33% TypeScript 99.01% HTML 0.66%

vite-plugin-web-extension's Introduction

Vite Plugin Web Extension

A simple but powerful Vite plugin for developing browser extensions

// vite.config.ts
import webExtension from "vite-plugin-web-extension";

export default defineConfig({
  plugins: [
    webExtension({
      manifest: path.resolve(__dirname, "manifest.json"),
      assets: "assets",
    }),
  ],
});

Features

  • ๐Ÿ”ง Automatically build inputs from in your manifest.json
  • โšก Super fast dev mode that automatically reloads your extension
  • ๐ŸŒ Supports all browsers
  • ๐Ÿ”ฅ Frontend frameworks for the popup, options page, and content scripts!
  • ๐Ÿค– Typescript support out of the box!
  • โœ… Manifest validation

Contributing

Special thanks to the contributors!

aklinker1
Aaron Klinker
xiz0r
Juan Colo
KentoNishi
Kento Nishi
knokmki612
Kimiaki Kuno
r2dev2
Ronak Badhe
x11x
x11x

See the contributing docs to setup the project for development.

Installation

npm i -D vite-plugin-web-extension

Roadmap

  • v0.1.0 Build for production
  • v0.2.0 CSS inputs & generated files
  • v0.3.0 Dev mode with automatic reload
  • v0.5.0 Manifest V3 support
  • v0.6.0 Frontend framework support in content scripts
  • v0.7.0 Browser specific flags in the manifest
  • v1.2.0 HMR for html pages

Setup and Usage

Lets say your project looks like this:

dist/
   build output...
src/
   assets/
      icon-16.png
      icon-48.png
      icon-128.png
   background/
      index.ts
   popup/
      index.html
   manifest.json
package.json
vite.config.ts
...

Here's the minimal setup required:

// vite.config.ts
import webExtension from "vite-plugin-web-extension";

export default defineConfig({
  root: "src",
  // Configure our outputs - nothing special, this is normal vite config
  build: {
    outDir: path.resolve(__dirname, "dist"),
    emptyOutDir: true,
  },
  // Add the webExtension plugin
  plugins: [
    webExtension({
      manifest: path.resolve(__dirname, "src/manifest.json"),
      assets: "assets",
    }),
  ],
});

Note that the assets option is relative to your Vite root. In this case, it's pointing to src/assets, not just assets.

You don't need to specify a root if you don't want to. When excluded, it defaults to the directory your vite.config.ts is in.

For the input manifest option, all paths should use their real file extension and the paths should be relative to your vite root.

// src/manifest.json
{
  "name": "Example",
  "version": "1.0.0",
  "manifest_version": "2",
  "icons": {
    // Relative to "src"
    "16": "assets/icon-16.png",
    "48": "assets/icon-48.png",
    "128": "assets/icon-128.png"
  },
  "browser_action": {
    "default_icon": "assets/icon-128.png",
    // Relative to "src"
    "default_popup": "popup/index.html"
  },
  "background": {
    // Relative to "src", using real .ts file extension
    "scripts": "background/index.ts"
  }
}

And there you go!

Run vite build and you should see a fully compiled and working browser extension in your dist/ directory!

How does this work?

The build process happens in 2 steps:

  1. Bundle all the HTML entry-points as a multi-page app
  2. Bundle everything else (background scripts/service worker, content scripts, etc) individually in library mode

Scripts have to be bundled individually, separate from each other and the HTML entry-points, because they cannot import additional JS files. Each entry-point needs to have everything it needs inside that one file listed in the final manifest.

Adding Frontend Frameworks

If you want to add a framework like Vue or React, just add their Vite plugin!

import vue from '@vitejs/plugin-vue'

export default defineConfig({
  ...
  plugins: [
    vue(),
    webExtension({ ... }),
  ],
});

You can now use the framework anywhere! In your popup, options page, content scripts, etc.

See demos/vue for a full example.

Advanced Features

Configuring Browser Startup

This plugin uses web-ext under the hood to startup a browser and install the extension in dev mode. You can configure web-ext via the webExtConfig option.

For a list of options, you'll have to look at web-ext's source code, and search for .command('run', then camelCase each flag. If it's type is array, set it equal to an array of the values.

Here are some examples (with their CLI equivalents above):

webExtension({
  webExtConfig: {
    // --chromium-binary /path/to/google-chrome
    chromiumBinary: "/path/to/google-chrome",
    // --start-url google.com --start-url duckduckgo.com
    startUrl: ["google.com", "duckduckgo.com"],
    // --watch-ignored *.md *.log
    watchIgnored: ["*.md", "*.log"],
  },
});

Also see #22 for a real use case of changing the startup chrome window size

Dev Mode

vite dev

will open a browser and install your extension.

When you change a file, it will either hot-reload (if the file was associated with an HTML entry point) or rebuilt and reload the entire extension (if the file is used by background/content scripts) so you get the latest code immediately.

Only supported for Manifest V2 builds due to a bug: https://bugs.chromium.org/p/chromium/issues/detail?id=1290188

Use Watch Mode for MV3 instead

Dev mode works best when you're using a front-end framework, and making changes to a UI like the popup or options page.

HMR will not be used when making changes to UI injected by content scripts.

Set disableAutoLaunch to true to skip the automatic installation of the extension.

Watch Mode

Watch mode differs from dev mode because it will rebuild and reload the entire extension on every file change (no HMR)

It will also result in the exact same code as vite build, whereas dev mode modifies your HTML files to enable HMR.

To run in watch mode, use the --watch flag.

vite build --watch

To reload when you update files other than source files (config files like tailwind.config.js) pass the watchFilePaths option. Use absolute paths for this option:

import path from "path";

export default defineConfig({
  ...
  plugins: [
    webExtension({
      watchFilePaths: [
        path.resolve(__dirname, "tailwind.config.js")
      ],
      disableAutoLaunch: false // default is false
    }),
  ],
});

Watch mode will not reload properly when the manifest changes. You'll need to restart the vite build --watch command use the updated manifest.

This is a limitation of web-ext

Set disableAutoLaunch to true to skip the automatic installation of the extension.

Additional Inputs

If you have have HTML or JS files that need to be built, but aren't listed in your manifest.json, you can add them via the additionalInputs option.

The paths should be relative to the Vite's root, just like the assets option.

export default defineConfig({
  plugins: [
    webExtension({
      ...
      additionalInputs: [
        "onboarding/index.html",
        "content-scripts/injected-from-background.ts",
      ]
    }),
  ],
});

CSS

For HTML entry points like the popup or options page, css is automatically output and referenced in the built HTML. There's nothing you need to do!

Manifest content_scripts

For content scripts listed in your manifest.json, it's a little more difficult. There are two ways to include CSS files:

  1. You have a CSS file in your project
  2. The stylesheet is generated by a framework like Vue or React or is imported by the code

For the first case, it's simple! Make sure you have the relevant plugin installed to parse your stylesheet (like scss), then list the file in your manifest.json. The plugin will look at the css array and output all inputs as plain CSS.

{
  "content_scripts": [
    {
      "matches": [...],
      "css": ["content-scripts/some-style.scss"]
    }
  ]
}

For the second case, it's a little more involved. Say your content script is at content-scripts/overlay.ts and is responsible for binding a Vue/React app to a webpage. When Vite compiles it, it will output two files: dist/content-scripts/overlay.js and dist/content-scripts/overlay.css. Check what is output, then update your manifest to point towards the output files, prefixed with generated:.

{
  "content_scripts": [
    {
      "matches": [...],
      "scripts": "content-scripts/overlay.ts",
      "css": ["generated:content-scripts/overlay.css"]
    }
  ]
}

This will tell the plugin that the file is already being generated for us, but that we still need it in the final manifest.

Browser API tabs.executeScripts

For content scripts injected programmatically, include the script's path in the plugin's additionalInputs option

Dynamic Manifests

The manifest option also accepts a function. This function should return a javascript object containing the same thing as manifest.json. It should include real file paths, as well as any browser specific flags (see next section).

Often times this is used to pull in details from your package.json like the version so they only have to be maintained in a single place

import webExtension from "vite-plugin-web-extension";

export default defineConfig({
  plugins: [
    webExtension({
      manifest: () => {
        // Generate your manifest
        const packageJson = require("./package.json");
        return {
          ...require("./manifest.json"),
          name: packageJson.name,
          version: packageJson.version,
        };
      },
      assets: "assets",
    }),
  ],
});

Browser Specific Manifest Fields

Either the file or object returned by the manifest option can include flags that specify certain fields for certain browsers, and the plugin will strip out any values that aren't for a specific browser

Here's an example: Firefox doesn't support manifest V3 yet, but chrome does!

{
  "{{chrome}}.manifest_version": 3,
  "{{firefox}}.manifest_version": 2,
  "{{chrome}}.action": {
    "default_popup": "index.html"
  },
  "{{firefox}}.browser_action": {
    "default_popup": "index.html",
    "browser_style": false
  },
  "options_page": "options.html",
  "permissions": ["activeTab", "{{firefox}}.<all_urls>"]
}

To build for a specific browser, simply pass the browser option and prefix any field name or string value with {{browser-name}}.. This is not limited to just chrome and firefox, you can use any string inside the double curly braces as long as your pass it into the plugin's browser option.

You can pass this option in a multitude of ways. Here's one way via environment variables!

# In package.json or via CLI
cross-env TARGET_BROWSER=chrome vite build
export default defineConfig({
  plugins: [
    webExtension({
      manifest: "manifest.json",
      assets: "assets",
      browser: process.env.TARGET_BROWSER,
    }),
  ],
});

Manifest Validation

Whenever your manifest is generated, it gets validated against Google's JSON schema: https://json.schemastore.org/chrome-manifest

To disable validation, pass the skipManifestValidation option:

export default defineConfig({
  plugins: [
    webExtension({
      skipManifestValidation: true,
    }),
  ],
});

Customize Lib Mode Builds

By default, vite-plugin-web-extension will automatically configure vite's build config when building the background and content scripts in lib mode.

If that config is does not work for your case, you can modify it via libModeViteConfig.

For example, if a script requires dynamic imports, they need to be added inline because UMD doesn't support code-splitting:

import { defineConfig } from "vite";
import webExtension from "vite-plugin-web-extension";

const libModeViteConfig = defineConfig({
  build: {
    rollupOptions: {
      output: {
        // Disable code splitting and put dynamic imports inline
        inlineDynamicImports: true,
      },
    },
  },
});

export default defineConfig({
  plugins: [
    webExtension({
      // ...
      libModeViteConfig,
    }),
  ],
});

vite-plugin-web-extension's People

Contributors

aklinker1 avatar github-actions[bot] avatar kentonishi avatar knokmki612 avatar r2dev2 avatar x11x avatar xiz0r avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.