Vite-plugin-web-extension - A vite plugin for generating cross browser platform, ES module based web extensions.

Overview

vite-plugin-web-extension

A vite plugin for generating cross browser platform, ES module based web extensions.

Features

  • Manifest V2 & V3 Support
  • Completely ES module based extensions
    • Including content scripts!
  • Vite based html and static asset handling
    • Including content scripts!
  • HMR support for all manifest properties
    • Including content scripts!
  • HMR support for CSS styles in content scripts
    • Including shadow DOM rendered content!

Usage

Quick Start

Build an example extension that uses this plugin:

With npm
npx degit https://github.com/samrum/vite-vue-web-extension vue-web-extension
cd vue-web-extension
npm install
npm run build
npm run serve:chrome
With pnpm
pnpm dlx degit https://github.com/samrum/vite-vue-web-extension vue-web-extension
cd vue-web-extension
pnpm install
pnpm build
pnpm serve:chrome

Vite Config

  • All manifest file names should be relative to the root of the project.
Manifest V2
import { defineConfig } from "vite";
import webExtension from "@samrum/vite-plugin-web-extension";

export default defineConfig({
  plugins: [
    webExtension({
      manifest: {
        name: pkg.name,
        description: pkg.description,
        version: pkg.version,
        manifest_version: 2,
        background: {
          scripts: ["src/background/script.js"],
        },
      },
    }),
  ],
});
Manifest V3
import { defineConfig } from "vite";
import webExtension from "@samrum/vite-plugin-web-extension";

export default defineConfig({
  plugins: [
    webExtension({
      manifest: {
        name: pkg.name,
        description: pkg.description,
        version: pkg.version,
        manifest_version: 3,
        background: {
          service_worker: "src/background/serviceWorker.js",
        },
      },
    }),
  ],
});

Content Scripts

  • For HMR style support within shadow DOMs, use the addStyleTarget function to add the shadowRoot of your element as a style target:

    if (import.meta.hot) {
      const { addStyleTarget } = await import("/@vite/client");
    
      addStyleTarget(shadowRoot);
    }
    
  • For builds, use the import.meta.CURRENT_CONTENT_SCRIPT_CSS_URL constant to reference the first generated CSS file associated with the current content script chunk.

Browser Support

The following requirements must be met by the browser:

  • Must support dynamic module imports made by web extension content scripts.
  • Must support import.meta.url

A sample of supported browsers:

Manifest V2 Manifest V3
Chromium 64 91
Firefox 89 N/A (In development)

The plugin will automatically default vite's build.target config option to these minimum browser versions if not already defined by the user.

How it works

The plugin will take the provided manifest, parse rollup input scripts from all supported manifest properties, then output an ES module based web extension.

This includes:

  • Generating and using a dynamic import wrapper script in place of original content scripts. Then, moving the original scripts to web_accessible_resources so they are accessible by the wrapper script. Needed because content scripts are not able to be loaded directly as ES modules.
    • This may expose your extension to fingerprinting by other extensions or websites. Manifest V3 supports a use_dynamic_url property that will mitigate this. This option is set for manifest V3 web accessible resources generated by this plugin.
  • Modifying Vite's static asset handling to maintain import.meta.url usages instead of rewriting to self.location. Needed so content script static asset handling can function correctly.
  • Modifying Vite's HMR client to add support for targeting specific elements as style injection locations. Needed to support HMR styles in shadow DOM rendered content.

Why this is a Vite specific plugin

The plugin relies on Vite to parse and handle html files in addition to relying on Vite's manifest generation in order to map generated files to the eventual extension manifest.

Development

This project uses pnpm for package management.

Lint

pnpm lint

Tests

pnpm test

Build

pnpm build
Comments
  • Maximum call stack size exceeded

    Maximum call stack size exceeded

    Hi @samrum

    I have an error trying to build a large extension, I know is an stupid Atlassian package that have over 20mb but would be great if you can fix it.

    the output i'm getting is:

    vite v3.1.8 building for production...
    ✓ 7285 modules transformed.
    rendering chunks (273)...[webExtension] Maximum call stack size exceeded
    

    And the function causing the problem is a recursive one which is on .../@samrum/vite-plugin-web-extension/dist/index.js

    getMetadataforChunk(chunkId, bundle, includeChunkAsAsset, metadata = {
            css: new Set(),
            assets: new Set(),
        })
    

    I tried to change the forEach loops with basic loops to see if I can pass the build, but with no luck.

    Can you take a look at it?

    Thank you very much!

    opened by aleksolutions 18
  • feat: web accessible scripts

    feat: web accessible scripts

    Adds ability to transpile scripts that user includes as web accessible resources. For instance, if a content script needs to inject an inpage script into the webpage at runtime, the user can simply include the path to the inpage script as a web_accessible_resource and the plugin will take care of the rest.

    This PR also adds a new plugin option webAccessibleScripts that simply mirrors the inputs to createFilter, allowing user to control which files will match as scripts:

    interface ViteWebExtensionOptions {
      webAccessibleScripts?: {
        include?: Pattern;
        exclude?: Pattern;
        options?: {
          resolve?: string | false | null;
        };
      };
    }
    
    opened by blake-regalia 9
  • CSS issue if importing some svelte in contentScript AND in options

    CSS issue if importing some svelte in contentScript AND in options

    I wanted to have a common TabBar.svelte used in the contentScript app as well as in the options App.

    So the file architecture look like

    /entries/contentScript/primary/App.svelte (import ~/lib/TabBar.svelte)
    /entries/options/App.svelte (import ~/lib/TabBar.svelte)
    /lib/TabBar.svelte
    

    The issue is as soon as the options App.svelte import the TabBar, the contentScript/primary/main.z2EEd.css is not longer generated upon build. For know I'm duplicating the file to prevent this issue, I've no clue how to fix this correctly though.

    My repo is here: https://github.com/HugoGresse/app-store-review-templates You'll need to import the App.svelte from the contentScript into options/App.svelte

    opened by HugoGresse 6
  • Content Security Issues

    Content Security Issues

    I am getting the following error:

    Screenshot 2022-09-14 at 12 13 52

    when:

    • bootstrapping via npm init @samrum/vite-plugin-web-extension@latest an manifest V3
    • Loading the extension manually or using pnpm run serve:chrome

    Additional Infos

    • OS: MacOS
    • Browser: Chrome Version 105.0.5195.102 (Official Build) (arm64) or Chrome Canary Version 107.0.5299.0 (Official Build) canary (arm64)
    • Package: @samrum/vite-plugin-web-extension: 2.0.0
    • Generated Manifest File:
    {
      "author": "....",
      "description": "....",
      "name": "....",
      "version": "0.0.1",
      "manifest_version": 3,
      "action": {
        "default_icon": {
          "16": "icons/16.png",
          "19": "icons/19.png",
          "32": "icons/32.png",
          "38": "icons/38.png"
        },
        "default_popup": "src/entries/popup/index.html"
      },
      "background": {
        "service_worker": "serviceWorker.js",
        "type": "module"
      },
      "content_scripts": [
        {
          "js": [
            "src/entries/contentScript/primary/main.js"
          ],
          "matches": [
            "*://*/*"
          ]
        }
      ],
      "host_permissions": [
        "*://*/*"
      ],
      "icons": {
        "16": "icons/16.png",
        "19": "icons/19.png",
        "32": "icons/32.png",
        "38": "icons/38.png",
        "48": "icons/48.png",
        "64": "icons/64.png",
        "96": "icons/96.png",
        "128": "icons/128.png",
        "256": "icons/256.png",
        "512": "icons/512.png"
      },
      "options_ui": {
        "page": "src/entries/options/index.html",
        "open_in_tab": true
      },
      "content_security_policy": {
        "extension_pages": "script-src 'self'  http://localhost:5173; object-src 'self'"
      }
    }
    
    opened by HaNdTriX 5
  • Error when running `npm run dev` on newly created extension project

    Error when running `npm run dev` on newly created extension project

    I created a brand new project just now using command. npm init @samrum/vite-plugin-web-extension@latest

    Selected V2+3 manifest. Selected React Selected Typescript

    When I run npm run dev i get an error.

      vite v2.9.5 dev server running at:
    
      > Local: http://localhost:3000/
      > Network: use `--host` to expose
    
      ready in 329ms.
    
    ✘ [ERROR] Could not resolve "/@vite/client"
    
        node_modules/@samrum/vite-plugin-web-extension/client.js:2:42:
          2 │   const { addStyleTarget } = await import("/@vite/client");
            â•ĩ                                           ~~~~~~~~~~~~~~~
    
    10:38:14 pm [vite] error while updating dependencies:
    Error: Build failed with 1 error:
    node_modules/@samrum/vite-plugin-web-extension/client.js:2:42: ERROR: Could not resolve "/@vite/client"
        at failureErrorWithLog (D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:1603:15)
        at D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:1249:28
        at runOnEndCallbacks (D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:1034:63)
        at buildResponseToResult (D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:1247:7)
        at D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:1356:14
        at D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:666:9
        at handleIncomingPacket (D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:763:9)
        at Socket.readFromStdout (D:\Dev\p\learn\webext1a\node_modules\esbuild\lib\main.js:632:7)
        at Socket.emit (node:events:526:28)
        at addChunk (node:internal/streams/readable:315:12)
    

    I am on Windows 10, node v16.14.2, npm 8.5.0

    After running npm run build both npm run serve:firefox and npm run serve:chrome seem to run fine. When running serve:firefox running a npm run build will reload an updated extension into firefox.

    I did notice the generated template code had typescript type errors, but that's a separate issue for later.

    opened by rluiten 5
  • HMR issues

    HMR issues

    Hi there!

    I ran into some issues with the HMR functionality: While watching for file changes and serving the extension on Chrome, the extension will reload itself entirely if a file has been changed, closing the pages I'm working on. On Firefox it won't reload the extension entirely, but I will need to manually refresh the page to see the changes.

    How to reproduce

    • Create a new project using npm init @samrum/vite-plugin-web-extension
    • Choose Manifest V2 + V3, Svelte and TypeScript
    • Run npm run watch and npm run serve:chrome parallelly
    • Open the extension options page in the browser and open the file /src/entries/options/App.svelte
    • Make changes in the file - the extension will reload itself entirely
    • You can also run npm run serve:firefox to see that HMR isn't working on Firefox

    Any suggestion how to overcome this?

    Thanks a lot for the work on this project! 🙏

    opened by guytepper 5
  • Build not emitting scripts included in web_accessible_resources

    Build not emitting scripts included in web_accessible_resources

    For example, input Manifest V3:

    {
      "web_accessible_resources": [
        {
          "resources": ["src/inpage.ts"],
          "matches": ["file://*/*", "http://*/*", "https://*/*"]
        }
      ]
    }
    

    Makes it into the output manifest.json, but the transpiled version of src/inpage.ts => src/inpage.{HASH}.js is never emitted in the output (and the path in the manifest still points to the typescript file).

    opened by blake-regalia 4
  • Question - content script

    Question - content script

    Is there a way so that scripts injected via content script like this:

    runtime.getURL('src/lib.ts')

    const script = document.createElement('script')
    script.type = 'module'
    script.src = browser.runtime.getURL('src/lib.ts')
    
    const head = document.head || document.getElementsByTagName('head')[0] || document.documentElement
    head.appendChild(script)
    

    are automatically added as well to web resources and paths resolved ?

    also looks like if i include script in web_accessible_resources its not being handled, i cant find it in production build

    	web_accessible_resources: [
    		{
    			matches: ['<all_urls>'],
    			resources: ['src/lib.ts'],
    		},
    	],
    
    opened by heathsnee 4
  • Importing from within isolated function

    Importing from within isolated function

    Not exactly sure how to approach this issue. Service Worker needs to execute a script dynamically using chrome.scripting.executeScript with the func parameter, providing a function that will be serialized, sent across frames, and reconstructed on the other side. This creates a problem for importing anything from within the content script.

    service-worker.ts

    import ContentScript from './content-script';
    
    chrome.runtime.onMessage.addListener((msg, sender) => {
      chrome.scripting.executeScript({
        target: {tabId:sender.tab.id},
        func: ContentScript,
        args: [secret],
      });
    });
    

    content-script.ts

    import { say } from 'cowsay';  // this will never work anyway since it binds outside the scope of the function
    
    export default async function(secret) {
      const { say } = await import('cowsay');  // this does not seem to work either
    
      console.log(say({text:secret});
    }
    
    opened by blake-regalia 4
  • Clarify MV3-in-Firefox support in README

    Clarify MV3-in-Firefox support in README

    It wasn't entirely clear to me from the README whether MV3 in Firefox is supported. Trying it out it seems like no? Since the serve:firefox script did not pass --firefox-preview mv3 to web-ext, the manifest.ts lists a service_worker (not yet supported by Firefox), and even after fixing those two issues, I get "Error: Extension is invalid".

    To set expectations, it would be nice if the README had an indication of the current status of support for MV3 in Firefox :)

    documentation enhancement 
    opened by Vinnl 3
  • How do you hot reload the extension?

    How do you hot reload the extension?

    I am aware of pnpm run build and pnpm run serve:firefox for running the build but is it possible to run the extension in a state where changes in the source will automatically be updated into the browser?

    opened by lassebomh 3
  • Dynamic content scripts and popups

    Dynamic content scripts and popups

    Using this plugin it seems to be impossible to have it compile content scripts or popups without also including them in the manifest. For dynamic content scripts (injected with executeScript), they shouldn't be listed in the manifest, although they do need to be available at a known location at runtime. For the popup, if you do specify a popup in the manifest, then there's no way to receive the "onClicked" event for the browser action. I would like to be able to receive the event and then dynamically decide whether to show the popup (or do something else).

    opened by Diggsey 2
  • Question: How to compile `content_scripts.css`?

    Question: How to compile `content_scripts.css`?

    I have this in my config

        content_scripts: [
          {
            matches: ['https://*.youtube.com/*'],
            js: ['src/inject.js'],
            css: ['src/inject.css'],
          },
        ],
    

    And I have a tailwind css file

    /* inject.css */
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    

    I expect the inject.css to be compiled but it is only copy pasted into dist/src. Is this possible? I'm new to vite and web extensions.

    opened by lemonadee71 3
  • Question: injection to MAIN environment

    Question: injection to MAIN environment

    For the extension that I'm building, I'm trying to inject to the MAIN environment

    chrome.scripting.registerContentScripts([
      {
        id: 'inpage',
        matches: ['http://*/*', 'https://*/*'],
        js: [
          'src/entries/content-scripts/inject.js'
        ],
        runAt: 'document_start',
        world: 'MAIN'
      },
    ]);
    

    but generated code (async()=>{await import(chrome.runtime.getURL("assets/src/entries/content-scripts/inject.8e490e7d.js"))})();

    raise an error chrome.runtime.getURL is not a function in console

    I understand that neither the file (inject.8e490e7d.js) nor the method (chrome.runtime.getURL) exists in MAIN context, but how to inject correctly?

    opened by Bighamster 3
  • Question: how to run icons through loaders?

    Question: how to run icons through loaders?

    For the extension that I'm building, I've got one core icon which is an SVG, and I was hoping to use vite-imagetools to process that into a bunch of different sized PNGs at build time, but I couldn't tell how to get the two vite plugins to work together.

    Is it possible to do something like #52 for the icons as well?

    question 
    opened by Alex-CS 1
Releases(v3.0.0)
Owner
Ruben Medina
Web developer. A place for some random things I've worked on.
Ruben Medina
Mobile app development framework and SDK using HTML5 and JavaScript. Create beautiful and performant cross-platform mobile apps. Based on Web Components, and provides bindings for Angular 1, 2, React and Vue.js.

Onsen UI - Cross-Platform Hybrid App and PWA Framework Onsen UI is an open source framework that makes it easy to create native-feeling Progressive We

null 8.7k Jan 4, 2023
Vue 3 + Vite + SSR template based on Vite Plugin SSR and inspired by Vitesse

Vite Vue SSR Starter Vue 3 + Vite + SSR template based on Vite Plugin SSR and inspired by Vitesse Features ⚡ī¸ Vue 3, Vite 2, TypeScript ?? Domain-Driv

Oleg Koval 10 Aug 2, 2022
Vue Native is a framework to build cross platform native mobile apps using JavaScript

Vue Native Visit our website at vue-native.io or read the official documentation here. Build native mobile apps using Vue Vue Native is a framework to

GeekyAnts 8.4k Jan 6, 2023
Build performant, native and cross-platform desktop applications with native Vue + powerful CSS like styling.🚀

Vue NodeGui Build performant, native and cross-platform desktop applications with Vue. ?? Vue NodeGUI is powered by Vue ?? and Qt5 ?? which makes it C

NodeGui 765 Dec 30, 2022
A Figma plugin for generating skeleton UI placeholders, specific to Discord's usecases.

Project Scaffold Generator Why? This is an internal tool we use at Discord to generate Discord specific "skeleton/placeholders" for help in designing

Bryan Berger 17 Nov 22, 2022
A swiss army knife with lots of tools, extensions and (scriptable) enhancements for Visual Studio Code.

vscode-powertools A swiss army knife with lots of tools, extensions and (scriptable) enhancements for Visual Studio Code. ⚠ī¸ ⚠ī¸ ⚠ī¸ NOTICE: If you have

e.GO Mobile 44 Nov 24, 2022
💖 Toolkit for generating sponsors images 😄

SponsorKit Toolkit for generating sponsors images. Usage Create .env file with: ; Token requires the `read:user` and `read:org` scopes. SPONSORKIT_GIT

Anthony Fu 395 Jan 2, 2023
Chrome Extension Boilerplate with SolidJS + Vite + TypeScript + Manifest V3 + Hot Relaod

Chrome Extension Boilerplate with SolidJS + Vite + TypeScript + Manifest V3 + Hot Relaod Intro This boilerplate is made for creating chrome extensions

fuyutarow 34 Dec 27, 2022
Chrome Extension boilerplate with SolidJS + Vite + CRXJS

SolidJS Chrome Extension boilerplate An easy way to create Chrome extensions with SolidJS using Vite + CRXJS. Features For more functionality, check o

Ryan Conceicao 8 Dec 25, 2022
🏝 Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all — including browsers?

Open Web Labs 14 Dec 24, 2022
🏝 Opinionated Web Components Starter template to help kick-start development of a cross-framework component library.

Web Component Library Starter Kit "Why create components for a specific framework when it can be written to be understood by all — including browsers?

Open Web Labs 5 May 1, 2022
A Marko plugin for Vite

@marko/vite A Marko plugin for Vite. Installation npm install @marko/vite Example config import { defineConfig } from "vite"; import marko from "@mark

Marko 49 Nov 26, 2022
Vite Svelte plugin to remove console logs in prod.

vite-plugin-svelte-console-remover A Vite plugin that removes all console statements (log, group, dir, error, etc) from Svelte, JS, and TS files durin

Josh Hubbard 29 Oct 13, 2022
A vite plugin that deletes console.log in the production environment

vite-plugin-remove-console A vite plugin that deletes console.log in the production environment English | 中文 Install npm install vite-plugin-remove-co

啝čŖŗ 49 Dec 22, 2022
A progress bar plugin for Vite.

vite-plugin-progress Display with progress bar when building ?? Install npm i vite-plugin-progress -D # yarn yarn add vite-plugin-progress -D # pn

Jeddy Gong 137 Dec 17, 2022
Laravel plugin for Vite.

Laravel Vite Plugin Introduction Vite is a modern frontend build tool that provides an extremely fast development environment and bundles your code fo

The Laravel Framework 580 Jan 7, 2023
🐝 A vite plugin automatically export files & HMR support

vite-plugin-hot-export Automatically export files with HMR English|įŽ€äŊ“中文 Why ? When developing, we often need to download some images or svg from the i

Frozen FIsh 54 Nov 12, 2022
Vite plugin for minifying / obfuscating CSS classes in production builds

vite-plugin-class-mangler Vite plugin for minifying / obfuscating classes in production builds. Compatible with Tailwind, inline, or imported styles.

Maxim 28 Dec 22, 2022
Rust dbg! in js powered by rollup/vite plugin system

rollup-plugin-dbg This plugin is also compatible with vite use with rollup import { defineConfig } from "rollup"; import config from "./package.json";

Jason 17 Aug 18, 2022