wxt icon indicating copy to clipboard operation
wxt copied to clipboard

[Experimental Feedback] Exclude `webextension-polyfill` via `extensionApi: "chrome"`

Open aklinker1 opened this issue 1 year ago • 12 comments

v0.19.0 introduced a new option for excluding the webextension-polyfill.

Setup

  1. Update your wxt.config.ts with the new feature flag:
    export default defineConfig({
      extensionApi: "chrome"
    })
    
  2. Install @types/chrome, the package providing types when using the chrome API. It provides more up-to-date types with much better support for MV3 features.
    $ pnpm i -D @types/chrome
    
  3. Regenerate project types:
    $ pnpm wxt prepare
    
  4. (Optional) Restart your editor or language server so your editor is using the new types
  5. (Optional) Ignore this step if you're using auto-imports. If you've disabled auto-imports, you'll need to update all your browser imports.

    If you skip this step, everything should still work, but your editor will continue using the old types instead of @types/chrome.

    - import { browser } from 'wxt/browser';
    + import { browser } from 'wxt/browser/chrome';
    

Testing

  • [ ] Are there any type errors? (Remember to run wxt prepare before this)
  • [ ] Any runtime errors in dev mode?
    $ wxt
    
  • [ ] Any runtime errors when loading production builds?
    $ wxt zip
    
  • [ ] If you're supporting Firefox/Safari, are there any runtime errors when ran there?

[!NOTE] If you have any feedback, problems, type errors, or runtime issues, please share them below so I can get them fixed. Goal is to ship this option as the default in the next major version, v0.20.0

aklinker1 avatar Jul 27 '24 20:07 aklinker1

import type { Commands } from "wxt/browser";

Is there a way to make code like this take advantage of the types in @types/chrome? Currently the types for "wxt/browser" are exported from webextension-polyfill.

aiktb avatar Jul 29 '24 13:07 aiktb

@aiktb If you installed @types/chrome, you should be able to access these types through the chrome global.

function registerCommand(command: chrome.commands.Command) {
  // ...
}

Because of the way the @types/chrome is written with namespaces, I can't really rename chrome to browser... But I'd prefer to export these types without using the word "chrome".

aklinker1 avatar Jul 29 '24 16:07 aklinker1

browser

Will test it out in my extension in a few and report back issues if I encounter them

Timeraa avatar Jul 29 '24 18:07 Timeraa

Tested and works just fine!

Timeraa avatar Jul 30 '24 14:07 Timeraa

Tested and works me too. If there's anything that have an impact at runtime, let me know, and I'll test it.

1natsu172 avatar Jul 30 '24 17:07 1natsu172

tested, works fine too :)

typed-sigterm avatar Aug 01 '24 06:08 typed-sigterm

What about aliasing webextension-polyfill to some wrapper (maybe a virtual entrypoint)? For example, I want to use webext-core packages, but they have dependencies on webextension-polyfill.

AsakuraMizu avatar Sep 19 '24 16:09 AsakuraMizu

What about aliasing webextension-polyfill to some wrapper (maybe a virtual entrypoint)? For example, I want to use webext-core packages, but they have dependencies on webextension-polyfill.

So there's two approaches:

  1. Just remove webextension-polyfill from WXT so it's not included in your extension when importing wxt/browser
  2. Setup an alias to completely remove the polyfill from ALL dependencies and sub-dependencies, like webext-core

extensionApi implements the first approach. That way if a dependency depends on the polyfill, it doesn't break the dependency.

If you want to fully remove the polyfill from all dependencies, you can setup the alias yourself:

// wxt.config.ts
export default defineConfig({
  vite: () => ({
    alias: {
      'webextension-polyfill': path.resolve('polyfill-replacement.ts'),
    },
    ssr: {
      noExternal: ['@webext-core/storage']
    },
  }),
});

You need to add all dependencies that rely on webextension-polyfill to the ssr.noExternal option so that Vite knows to process those modules and have the alias take effect.

// polyfill-replacement.ts
import { browser } from 'wxt/browser/chrome';

export default browser;

But be aware this can break any dependencies that rely on polyfill-specific behaviors. This WAS the behavior with the old experimental option before this one, but I changed it to just remove the polyfill from WXT to avoid breaking other dependencies.

aklinker1 avatar Sep 19 '24 21:09 aklinker1

@aklinker1 think I've found a bug with type generation using this feature:

browser.runtime.getURL isn't available at all - not really sure why as I'd think path.d.ts would correctly override the value in index.d.ts, which chrome.d.ts would then use? Maybe the fact that we are importing WxtRuntime changes TypeScript's behaviour?

FYI I'm on the latest TS version as of now (5.6.2).

joealden avatar Sep 20 '24 14:09 joealden

@joealden I also had a similar issue in one of my work repos. The problem was that I wasn't extending the .wxt/tsconfig.json.

So make sure either it's extended in your tsconfig:

{
  "extends": "./.wxt/tsconfig.json"
}

or make sure to include .wxt/wxt.d.ts somewhere else in your typescript project:

/// <reference types="./.wxt/wxt.d.ts" />

aklinker1 avatar Sep 20 '24 14:09 aklinker1

@aklinker1 thanks! Is that documented (as I presume it'd not specific to wxt/browser/chrome but wxt/browser too and any other type-gen APIs)? As I followed https://wxt.dev/get-started/installation.html#from-scratch and don't see a mention of this (or in https://wxt.dev/guide/key-concepts/web-extension-polyfill.html)?

joealden avatar Sep 20 '24 14:09 joealden

I have it documented in a big rewrite I'm doing: https://github.com/wxt-dev/wxt/blob/docs-structure-update/docs/guide/config/typescript.md

Will add the part about the declaration file though.

aklinker1 avatar Sep 20 '24 17:09 aklinker1

Fantastic upgrade strategy and the webextension-polyfill situation handling in general @aklinker1 🎖️

Reporting no issues in a simple project, will follow up once it grows ✔️

jalooc avatar Oct 18 '24 13:10 jalooc

Seems like this is working well for people, I'm gonna enable extensionApi: "chrome" in new projects by updating the templates. This will get a few more people to test this out.

  • https://github.com/wxt-dev/wxt/pull/1083

aklinker1 avatar Oct 19 '24 07:10 aklinker1

I might have forgotten to give my input on this. Using it in latest release of my extension for Chrome and Firefox. Working flawlessly for ~60k users.

nonwip avatar Oct 19 '24 12:10 nonwip

@aklinker1 We found a problem about the extensionApi . https://github.com/wxt-dev/wxt/issues/1116

1natsu172 avatar Oct 28 '24 06:10 1natsu172

@aklinker1 We found a problem about the extensionApi . https://github.com/wxt-dev/wxt/issues/1116

Huh, weird... @types/chrome is a direct dependency, so PNPM should be able to find it...

https://github.com/wxt-dev/wxt/blob/main/packages%2Fwxt%2Fpackage.json#L85

Quick fix, let's add the dependency to the templates for now.

aklinker1 avatar Oct 28 '24 12:10 aklinker1

Triple slash directives are removed at packages/wxt/dist/browser/chrome.d.ts to build. And pnpm will hoist @type/chrome. WXT can't find the path because didn't explicitly import it...…The theory is this, but not sure how to consider pnpm. 🤷

Agree to include it in the dependencies as a workaround for now.

1natsu172 avatar Oct 28 '24 13:10 1natsu172

@aklinker1 I submitted workaround PR. https://github.com/wxt-dev/wxt/pull/1119

1natsu172 avatar Oct 28 '24 16:10 1natsu172

@aklinker1

or make sure to include .wxt/wxt.d.ts somewhere else in your typescript project:

could you please elaborate on what is meant with "include somewhere else"? The <reference.../> XML from the docs is confusing, as ts configs are usually json.

So not sure if I understand it correctly, but I've put it into the root tsconfig.base.json as

"include": ["./.wxt/wxt.d.ts"]

Haven't had any issues so far, other than IntelliJ-related issues with linting, so I guess it's not totally wrong

mklueh avatar Oct 29 '24 17:10 mklueh

@mklueh

could you please elaborate on what is meant with "include somewhere else"? The <reference.../> XML from the docs is confusing, as ts configs are usually json.

https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html

It means to include that directive somewhere at the global level.

2wheeh avatar Nov 04 '24 02:11 2wheeh

@mklueh

could you please elaborate on what is meant with "include somewhere else"? The <reference.../> XML from the docs is confusing, as ts configs are usually json.

https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html

It means to include that directive somewhere at the global level.

Thanks.

To be honest using this feels totally wrong.

Is there any reason why it can't be added via tsconfig.includes in the extension's tsconfig.json?

{
  "extends": "../../../tsconfig.base.json",
  "includes": [
    ".wxt/wxt.d.ts"
  ]
}

Looks that it does what it should and its much cleaner than putting these references in files other than tsconfig

image

But maybe I'm missing the point here...

mklueh avatar Nov 04 '24 06:11 mklueh

Yup, that works too. I only recommend references because that way you don't have to worry about merging your includes in the tsconfig file if it extends another TSConfig.

aklinker1 avatar Nov 13 '24 19:11 aklinker1

Hi @aklinker1 , I encountered something related to this which might be of interest. I was using @types/chrome and it worked for most of the things. However at one place when I wanted to use a firefox specific property, but typescript complained that the property is not available. Here's a relevant example:

  browser.tabs.onActivated.addListener((activeInfo) => {
    console.log(activeInfo.previousTabId);
  });

The previousTabId is not available in chrome, only in firefox. This property is not shown when using @types/chrome, but works with webextension-polyfill. What will be the right way in this case? I know I should check the api availability as per docs, and with that a @ts-ignore will be good ?

aabidk20 avatar Jan 06 '25 14:01 aabidk20

@aabidk20 in this case, I'd recommend not relying on the Firefox-only property if you're supporting chrome as well. That way you only have to manage one implementation, even if it is more complex.

But if you want to keep using it, yes, @ts-ignoreing it is probably the right thing to do.

aklinker1 avatar Jan 06 '25 14:01 aklinker1

Ok, that's good to know. Thanks!

aabidk20 avatar Jan 06 '25 18:01 aabidk20

Alright, thanks for your feedback everyone, checkout v0.20.0 for the new browser object based on @types/chrome.

aklinker1 avatar Mar 28 '25 16:03 aklinker1