Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 719192b1aa | |||
| 7eb32bdac8 | |||
| 862756fd5b | |||
| 5ba16e72e0 | |||
| e43ae0504d | |||
| acb6cd180c | |||
| a3301d0413 | |||
| b9e72358ac | |||
| 61b42ef326 | |||
| da9e27c28d | |||
| 6f80fbabaa | |||
| d2308bd45c | |||
| d35972d24f | |||
| 298a264934 | |||
| ad63b595b4 | |||
| 986a9ceebf | |||
| 682b65e723 | |||
| cb661506c0 | |||
| 505aa06443 | |||
| f534a978f1 | |||
| 1293815934 | |||
| 0c0fd18bf4 | |||
| c49c31813f | |||
| 877ab11bb4 | |||
| 0d18d8d7e3 | |||
| 5f977857a1 | |||
| a63b21d417 | |||
| e1b7d6090e | |||
| 1bcc5cdd81 | |||
| 873fdbc4b3 | |||
| 79128effea | |||
| 14ef94fc09 | |||
| 759e3e3a4d | |||
| 5b7b16efba | |||
| 7fd171acb5 | |||
| 02ed3f1d44 | |||
| 4f7f3f36a3 | |||
| 73a8cf12a9 | |||
| 5f5eb16705 | |||
| f0f4437870 | |||
| 868c042511 | |||
| bdb49c0b3d | |||
| cc3c43d1bd | |||
| e47131efc1 | |||
| 82d8024fb4 | |||
| c662a59d85 | |||
| 4e50fb1f92 | |||
| fbae370d1e | |||
| 9c64c9dd61 | |||
| 8f6dba20d2 | |||
| f531fcd5c3 | |||
| ff1720a4c6 | |||
| d62203dff8 | |||
| 99b5076d69 | |||
| 298e7101f7 | |||
| b4ce36b708 | |||
| 5d096a4a92 | |||
| e54df0aca8 | |||
| 2e8baf0161 | |||
| c9dca0222c | |||
| 6044ab73bd | |||
| 660945c792 | |||
| bcb20874a8 | |||
| 83ad0e3ff0 | |||
| aad17c8d26 | |||
| b0ef178c9c | |||
| 0175c430a3 | |||
| 2776587392 | |||
| b978465d7a | |||
| f7989ea1e0 | |||
| 0cf34d3170 | |||
| 4fe04c6f8a |
@@ -16,6 +16,7 @@ on:
|
||||
- module-vue
|
||||
- storage
|
||||
- unocss
|
||||
- webextension-polyfill
|
||||
- wxt
|
||||
|
||||
permissions:
|
||||
|
||||
@@ -15,6 +15,7 @@ on:
|
||||
- module-svelte
|
||||
- module-vue
|
||||
- storage
|
||||
- webextension-polyfill
|
||||
- wxt
|
||||
|
||||
permissions:
|
||||
|
||||
@@ -36,8 +36,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
commit_message: 'fix: Upgrade \`@wxt-dev/browser\` to latest \`@types/chrome\` version'
|
||||
file_pattern: 'packages/browser/**'
|
||||
commit_message: 'fix: Upgrade `@wxt-dev/browser` to latest `@types/chrome` version'
|
||||
|
||||
- name: Publish Package
|
||||
if: steps.commit.outputs.changes_detected == 'true'
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
.wxt
|
||||
*.log
|
||||
/docs/.vitepress/cache
|
||||
docs/.vitepress/.temp
|
||||
coverage
|
||||
dist
|
||||
node_modules
|
||||
|
||||
@@ -174,3 +174,13 @@ npm i https://pkg.pr.new/@wxt-dev/module-react@main
|
||||
# Install `@wxt-dev/storage` from a specific commit:
|
||||
npm i https://pkg.pr.new/@wxt-dev/module-react@426f907
|
||||
```
|
||||
|
||||
## Blog Posts
|
||||
|
||||
Anyone is welcome to submit a blog post on https://wxt.dev/blog!
|
||||
|
||||
> [!NOTE]
|
||||
> Before starting on a blog post, please message Aaron on Discord or start a discussion on GitHub to get permission to write about a topic, but most topics are welcome: Major version updates, tutorials, etc.
|
||||
|
||||
- **English only**: Blog posts should be written in English. Unfortunately, our maintainers doesn't have the bandwidth right now to translate our docs, let alone blog posts. Sorry 😓
|
||||
- **AI**: Please only use AI to translate or proof-read your blog post. Don't generate the whole thing... We don't want to publish that.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
// @ts-expect-error: Vitepress data-loader magic, this import is correct
|
||||
import { data } from '../loaders/blog.data';
|
||||
import BlogPostPreview from './BlogPostPreview.vue';
|
||||
|
||||
const posts = computed(() =>
|
||||
data
|
||||
.map((post) => ({
|
||||
...post,
|
||||
...post.frontmatter,
|
||||
date: new Date(post.frontmatter.date),
|
||||
}))
|
||||
.sort((a, b) => b.date.getTime() - a.date.getTime()),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div>
|
||||
<div class="vp-doc">
|
||||
<h1>Blog</h1>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<BlogPostPreview v-for="post of posts" :key="post.url" :post />
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.container > div {
|
||||
padding: 32px;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
list-style: none;
|
||||
}
|
||||
ul,
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul li {
|
||||
padding-top: 16px;
|
||||
margin-top: 16px;
|
||||
border-top: 1px solid var(--vp-c-default);
|
||||
}
|
||||
ul li:last-child {
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--vp-c-default);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<script lang="ts" setup>
|
||||
import useBlogDate from '../composables/useBlogDate';
|
||||
import { useData } from 'vitepress';
|
||||
|
||||
const { frontmatter } = useData();
|
||||
const date = useBlogDate(() => frontmatter.value.date);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-doc">
|
||||
<main class="container-content">
|
||||
<h1 v-html="$frontmatter.title" />
|
||||
<p class="meta-row">
|
||||
<a
|
||||
class="author"
|
||||
v-for="author of $frontmatter.authors"
|
||||
:key="author.github"
|
||||
:href="`https://github.com/${author.github}`"
|
||||
>
|
||||
<img :src="`https://github.com/${author.github}.png?size=96`" />
|
||||
<span>{{ author.name }}</span>
|
||||
</a>
|
||||
<span>•</span>
|
||||
<span>{{ date }}</span>
|
||||
</p>
|
||||
<Content />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
vp-doc {
|
||||
display: flex;
|
||||
}
|
||||
main {
|
||||
max-width: 1080px;
|
||||
padding: 32px;
|
||||
margin: auto;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
main {
|
||||
padding: 64px;
|
||||
}
|
||||
}
|
||||
.meta-row {
|
||||
display: flex;
|
||||
color: var(--vp-c-text-2);
|
||||
gap: 16px;
|
||||
overflow: hidden;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
.meta-row > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.author {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
color: var(--vp-c-text-2);
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
.author img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
.author span {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.author:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts" setup>
|
||||
import useBlogDate from '../composables/useBlogDate';
|
||||
|
||||
const props = defineProps<{
|
||||
post: {
|
||||
title: string;
|
||||
description?: string;
|
||||
date: Date;
|
||||
url: string;
|
||||
authors: Array<{ name: string; github: string }>;
|
||||
};
|
||||
}>();
|
||||
|
||||
const date = useBlogDate(() => props.post.date);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li class="blog-list-item">
|
||||
<a :href="post.url">
|
||||
<div class="vp-doc">
|
||||
<h3 class="title" v-html="post.title" />
|
||||
<p class="description" v-html="post.description" />
|
||||
<p class="meta">
|
||||
{{ post.authors.map((author) => author.name).join(', ') }}
|
||||
•
|
||||
{{ date }}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
li > a > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 -16px;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
li > a > div:hover {
|
||||
background: var(--vp-c-default);
|
||||
}
|
||||
li .title {
|
||||
color: var(--vp-c-text);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
li .description {
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
li .meta {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
</style>
|
||||
@@ -70,6 +70,7 @@ const chromeExtensionIds = [
|
||||
'hafcajcllbjnoolpfngclfmmgpikdhlm', // Monochromate
|
||||
'bmoggiinmnodjphdjnmpcnlleamkfedj', // AliasVault - Open-Source Password & (Email) Alias Manager
|
||||
'hlnhhamckimoaiekbglafiebkfimhapb', // SnapThePrice: AI-Powered Real-time Lowest Price Finder
|
||||
'gdjampjdgjmbifnhldgcnccdjkcoicmg', // radiofrance - news & broadcasts (French), music (international)
|
||||
];
|
||||
|
||||
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { computed, toValue, MaybeRefOrGetter } from 'vue';
|
||||
|
||||
const MONTH_FORMATTER = new Intl.DateTimeFormat(
|
||||
globalThis?.navigator?.language,
|
||||
{
|
||||
month: 'long',
|
||||
},
|
||||
);
|
||||
|
||||
export default function (date: MaybeRefOrGetter<Date | string>) {
|
||||
return computed(() => {
|
||||
const d = new Date(toValue(date));
|
||||
return `${MONTH_FORMATTER.format(d)} ${d.getDate()}, ${d.getFullYear()}`;
|
||||
});
|
||||
}
|
||||
+57
-15
@@ -1,4 +1,4 @@
|
||||
import { defineConfig } from 'vitepress';
|
||||
import { DefaultTheme, defineConfig } from 'vitepress';
|
||||
import typedocSidebar from '../api/reference/typedoc-sidebar.json';
|
||||
import {
|
||||
menuGroup,
|
||||
@@ -15,20 +15,25 @@ import { version as autoIconsVersion } from '../../packages/auto-icons/package.j
|
||||
import { version as unocssVersion } from '../../packages/unocss/package.json';
|
||||
import { version as storageVersion } from '../../packages/storage/package.json';
|
||||
import { version as analyticsVersion } from '../../packages/analytics/package.json';
|
||||
import knowledge from 'vitepress-knowledge';
|
||||
import addKnowledge from 'vitepress-knowledge';
|
||||
import {
|
||||
groupIconMdPlugin,
|
||||
groupIconVitePlugin,
|
||||
localIconLoader,
|
||||
} from 'vitepress-plugin-group-icons';
|
||||
import { Feed } from 'feed';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const origin = 'https://wxt.dev';
|
||||
|
||||
const title = 'Next-gen Web Extension Framework';
|
||||
const titleSuffix = ' – WXT';
|
||||
const description =
|
||||
"WXT provides the best developer experience, making it quick, easy, and fun to develop web extensions. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
|
||||
const ogTitle = `${title}${titleSuffix}`;
|
||||
const ogUrl = 'https://wxt.dev';
|
||||
const ogImage = 'https://wxt.dev/social-preview.png';
|
||||
const ogUrl = origin;
|
||||
const ogImage = `${origin}/social-preview.png`;
|
||||
|
||||
const otherPackages = {
|
||||
analytics: analyticsVersion,
|
||||
@@ -38,18 +43,25 @@ const otherPackages = {
|
||||
unocss: unocssVersion,
|
||||
};
|
||||
|
||||
const knowledge = addKnowledge<DefaultTheme.Config>({
|
||||
serverUrl: 'https://knowledge.wxt.dev',
|
||||
paths: {
|
||||
'/': 'docs',
|
||||
'/api/': 'api-reference',
|
||||
'/blog/': 'blog',
|
||||
},
|
||||
layoutSelectors: {
|
||||
blog: '.container-content',
|
||||
},
|
||||
pageSelectors: {
|
||||
'examples.md': '#VPContent > .VPPage',
|
||||
'blog.md': '#VPContent > .VPPage',
|
||||
},
|
||||
});
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
extends: knowledge({
|
||||
serverUrl: 'https://knowledge.wxt.dev',
|
||||
paths: {
|
||||
'/': 'docs',
|
||||
'/api/': 'api-reference',
|
||||
},
|
||||
pageSelectors: {
|
||||
'examples.md': '#VPContent > .VPPage',
|
||||
},
|
||||
}),
|
||||
extends: knowledge,
|
||||
|
||||
titleTemplate: `:title${titleSuffix}`,
|
||||
title: 'WXT',
|
||||
@@ -69,7 +81,33 @@ export default defineConfig({
|
||||
},
|
||||
lastUpdated: true,
|
||||
sitemap: {
|
||||
hostname: 'https://wxt.dev',
|
||||
hostname: origin,
|
||||
},
|
||||
|
||||
async buildEnd(site) {
|
||||
// @ts-expect-error: knowledge.buildEnd is not typed, but it exists.
|
||||
await knowledge.buildEnd(site);
|
||||
|
||||
// Only construct the RSS document for production builds
|
||||
const { default: blogDataLoader } = await import('./loaders/blog.data');
|
||||
const posts = await blogDataLoader.load();
|
||||
const feed = new Feed({
|
||||
copyright: 'MIT',
|
||||
id: 'wxt',
|
||||
title: 'WXT Blog',
|
||||
link: `${origin}/blog`,
|
||||
});
|
||||
posts.forEach((post) => {
|
||||
feed.addItem({
|
||||
date: post.frontmatter.date,
|
||||
link: new URL(post.url, origin).href,
|
||||
title: post.frontmatter.title,
|
||||
description: post.frontmatter.description,
|
||||
});
|
||||
});
|
||||
// console.log('rss.xml:');
|
||||
// console.log(feed.rss2());
|
||||
await writeFile(join(site.outDir, 'rss.xml'), feed.rss2(), 'utf8');
|
||||
},
|
||||
|
||||
head: [
|
||||
@@ -90,6 +128,9 @@ export default defineConfig({
|
||||
md.use(footnote);
|
||||
md.use(groupIconMdPlugin);
|
||||
},
|
||||
languageAlias: {
|
||||
mjs: 'js',
|
||||
},
|
||||
},
|
||||
|
||||
themeConfig: {
|
||||
@@ -126,6 +167,7 @@ export default defineConfig({
|
||||
navItem('Guide', '/guide/installation'),
|
||||
navItem('Examples', '/examples'),
|
||||
navItem('API', '/api/reference/wxt'),
|
||||
navItem('Blog', '/blog'),
|
||||
navItem(`v${wxtVersion}`, [
|
||||
navItem('wxt', [
|
||||
navItem(`v${wxtVersion}`, '/'),
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { createContentLoader } from 'vitepress';
|
||||
|
||||
export default createContentLoader('blog/*.md');
|
||||
@@ -3,15 +3,18 @@ import Icon from '../components/Icon.vue';
|
||||
import EntrypointPatterns from '../components/EntrypointPatterns.vue';
|
||||
import UsingWxtSection from '../components/UsingWxtSection.vue';
|
||||
import ExampleSearch from '../components/ExampleSearch.vue';
|
||||
import BlogLayout from '../components/BlogLayout.vue';
|
||||
import './custom.css';
|
||||
import 'virtual:group-icons.css';
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
enhanceApp(ctx) {
|
||||
ctx.app.component('Icon', Icon);
|
||||
ctx.app.component('EntrypointPatterns', EntrypointPatterns);
|
||||
ctx.app.component('UsingWxtSection', UsingWxtSection);
|
||||
ctx.app.component('ExampleSearch', ExampleSearch);
|
||||
ctx.app
|
||||
.component('Icon', Icon)
|
||||
.component('EntrypointPatterns', EntrypointPatterns)
|
||||
.component('UsingWxtSection', UsingWxtSection)
|
||||
.component('ExampleSearch', ExampleSearch)
|
||||
.component('blog', BlogLayout);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
layout: page
|
||||
---
|
||||
|
||||
<script lang="ts" setup>
|
||||
import BlogHome from './.vitepress/components/BlogHome.vue';
|
||||
</script>
|
||||
|
||||
<BlogHome />
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Real World Messaging
|
||||
description: |
|
||||
The extension messaging APIs are difficult to learn. Let's go beyond the simple examples from Chrome and Firefox's documentation to build our own simple messaging system from scratch.
|
||||
authors:
|
||||
- name: Aaron Klinker
|
||||
github: aklinker1
|
||||
date: 2024-10-20T04:54:23.601Z
|
||||
---
|
||||
|
||||
Test content **bold** _italic_
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
layout: blog
|
||||
title: Introducing <code>#imports</code>
|
||||
description: Learn how WXT's new <code>#imports</code> module works and how to use it.
|
||||
authors:
|
||||
- name: Aaron Klinker
|
||||
github: aklinker1
|
||||
date: 2024-12-06T14:39:00.000Z
|
||||
---
|
||||
|
||||
WXT v0.20 introduced a new way of manually importing its APIs: **the `#imports` module**. This module was introduced to simplify import statements and provide more visibility into all the APIs WXT provides.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
import { browser } from 'wxt/browser'; // [!code --]
|
||||
import { createShadowRootUi } from 'wxt/utils/content-script-ui/shadow-root'; // [!code --]
|
||||
import { defineContentScript } from 'wxt/utils/define-content-script'; // [!code --]
|
||||
import { injectScript } from 'wxt/utils/inject-script'; // [!code --]
|
||||
import { // [!code ++]
|
||||
browser, createShadowRootUi, defineContentScript, injectScript // [!code ++]
|
||||
} from '#imports'; // [!code ++]
|
||||
```
|
||||
|
||||
The `#imports` module is considered a "virtual module", because the file doesn't actually exist. At build-time, imports are split into individual statements for each API:
|
||||
|
||||
:::code-group
|
||||
|
||||
```ts [What you write]
|
||||
import { defineContentScript, injectScript } from '#imports';
|
||||
```
|
||||
|
||||
```ts [What the bundler sees]
|
||||
import { defineContentScript } from 'wxt/utils/define-content-script';
|
||||
import { injectScript } from 'wxt/utils/inject-script';
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Think of `#imports` as a convenient way to access all of WXT's APIs from one place, without impacting performance or bundle size.
|
||||
|
||||
This enables better tree-shaking compared to v0.19 and below.
|
||||
|
||||
:::tip Need to lookup the full import path of an API?
|
||||
Open up your project's `.wxt/types/imports-module.d.ts` file.
|
||||
:::
|
||||
|
||||
## Mocking
|
||||
|
||||
When writing tests, you might need to mock APIs from the `#imports` module. While mocking these APIs is very easy, it may not be immediately clear how to accomplish it.
|
||||
|
||||
Let's look at an example using Vitest. When [configured with `wxt/testing`](/guide/essentials/unit-testing#vitest), Vitest sees the same transformed code as the bundler. That means to mock an API from `#imports`, you need to call `vi.mock` with the real import path, not `#imports`:
|
||||
|
||||
```ts
|
||||
import { injectScript } from '#imports';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
vi.mock('wxt/utils/inject-script')
|
||||
const injectScriptMock = vi.mocked(injectScript);
|
||||
|
||||
injectScriptMock.mockReturnValueOnce(...);
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
You don't have to use `#imports` if you don't like - you can continue importing APIs from their submodules. However, using `#imports` is the recommended approach moving forwards.
|
||||
|
||||
- As more APIs are added, you won't have to memorize additional import paths.
|
||||
- If breaking changes are made to import paths in future major versions, `#imports` won't break.
|
||||
|
||||
Happy Coding 😄
|
||||
|
||||
> P.S. Yes, this is exactly how [Nuxt's `#imports`](https://nuxt.com/docs/guide/concepts/auto-imports#explicit-imports) works! We use the exact same library, [`unimport`](https://github.com/unjs/unimport).
|
||||
|
||||
---
|
||||
|
||||
[Discuss this blog post on Github](https://github.com/wxt-dev/wxt/discussions/1543).
|
||||
@@ -46,7 +46,7 @@ import image from '~/assets/image.png';
|
||||
|
||||
## `/public` Directory
|
||||
|
||||
Files inside `<srcDir>/public/` are copied into the output folder as-is, without being processed by WXT's bundler.
|
||||
Files inside `<rootDir>/public/` are copied into the output folder as-is, without being processed by WXT's bundler.
|
||||
|
||||
Here's how you access them:
|
||||
|
||||
@@ -81,6 +81,10 @@ img.src = imageUrl;
|
||||
|
||||
:::
|
||||
|
||||
:::warning
|
||||
Assets in the `public/` directory are **_not_** accessible in content scripts by default. To use a public asset in a content script, you must add it to your manifest's [`web_accessible_resources` array](/api/reference/wxt/type-aliases/UserManifest#web-accessible-resources).
|
||||
:::
|
||||
|
||||
## Inside Content Scripts
|
||||
|
||||
Assets inside content scripts are a little different. By default, when you import an asset, it returns just the path to the asset. This is because Vite assumes you're loading assets from the same hostname.
|
||||
|
||||
@@ -11,19 +11,7 @@ export default defineConfig({
|
||||
});
|
||||
```
|
||||
|
||||
By default, WXT sets up auto-imports for all of it's own APIs:
|
||||
|
||||
- [`browser`](/api/reference/wxt/browser/variables/browser) from `wxt/browser`
|
||||
- [`defineContentScript`](/api/reference/wxt/sandbox/functions/defineContentScript) from `wxt/sandbox`
|
||||
- [`defineBackground`](/api/reference/wxt/sandbox/functions/defineBackground) from `wxt/sandbox`
|
||||
- [`defineUnlistedScript`](/api/reference/wxt/sandbox/functions/defineUnlistedScript) from `wxt/sandbox`
|
||||
- [`createIntegratedUi`](/api/reference/wxt/client/functions/createIntegratedUi) from `wxt/client`
|
||||
- [`createShadowRootUi`](/api/reference/wxt/client/functions/createShadowRootUi) from `wxt/client`
|
||||
- [`createIframeUi`](/api/reference/wxt/client/functions/createIframeUi) from `wxt/client`
|
||||
- [`fakeBrowser`](/api/reference/wxt/testing/variables/fakeBrowser) from `wxt/testing`
|
||||
- And more!
|
||||
|
||||
WXT also adds some project directories as auto-import sources automatically:
|
||||
By default, WXT automatically sets up auto-imports for all of it's own APIs and some of your project directories:
|
||||
|
||||
- `<srcDir>/components/*`
|
||||
- `<srcDir>/composables/*`
|
||||
@@ -32,6 +20,8 @@ WXT also adds some project directories as auto-import sources automatically:
|
||||
|
||||
All named and default exports from files in these directories are available everywhere else in your project without having to import them.
|
||||
|
||||
To see the complete list of auto-imported APIs, run [`wxt prepare`](/api/cli/wxt-prepare) and look at your project's `.wxt/types/imports-module.d.ts` file.
|
||||
|
||||
## TypeScript
|
||||
|
||||
For TypeScript and your editor to recognize auto-imported variables, you need to run the [`wxt prepare` command](/api/cli/wxt-prepare).
|
||||
@@ -110,3 +100,19 @@ export default defineConfig({
|
||||
imports: false, // [!code ++]
|
||||
});
|
||||
```
|
||||
|
||||
## Explicit Imports (`#imports`)
|
||||
|
||||
You can manually import all of WXT's APIs via the `#imports` module:
|
||||
|
||||
```ts
|
||||
import {
|
||||
createShadowRootUi,
|
||||
ContentScriptContext,
|
||||
MatchPattern,
|
||||
} from '#imports';
|
||||
```
|
||||
|
||||
To learn more about how the `#imports` module works, read the [related blog post](/blog/2024-12-06-using-imports-module).
|
||||
|
||||
If you've disabled auto-imports, you should still use `#imports` to import all of WXT's APIs from a single place.
|
||||
|
||||
@@ -4,7 +4,7 @@ outline: deep
|
||||
|
||||
# Browser Startup
|
||||
|
||||
> See the [API Reference](/api/reference/wxt/interfaces/ExtensionRunnerConfig) for a full list of config.
|
||||
> See the [API Reference](/api/reference/wxt/interfaces/WebExtConfig) for a full list of config.
|
||||
|
||||
During development, WXT uses [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext) to automatically open a browser window with your extension installed.
|
||||
|
||||
@@ -14,15 +14,15 @@ You can configure browser startup in 3 places:
|
||||
|
||||
1. `<rootDir>/web-ext.config.ts`: Ignored from version control, this file lets you configure your own options for a specific project without affecting other developers
|
||||
|
||||
```ts
|
||||
import { defineRunnerConfig } from 'wxt';
|
||||
```ts [web-ext.config.ts]
|
||||
import { defineWebExtConfig } from 'wxt';
|
||||
|
||||
export default defineRunnerConfig({
|
||||
export default defineWebExtConfig({
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
2. `<rootDir>/wxt.config.ts`: Via the [`runner` config](/api/reference/wxt/interfaces/InlineConfig#runner), included in version control
|
||||
2. `<rootDir>/wxt.config.ts`: Via the [`webExt` config](/api/reference/wxt/interfaces/InlineConfig#webext), included in version control
|
||||
3. `$HOME/web-ext.config.ts`: Provide default values for all WXT projects on your computer
|
||||
|
||||
## Recipes
|
||||
@@ -31,8 +31,8 @@ You can configure browser startup in 3 places:
|
||||
|
||||
To set or customize the browser opened during development:
|
||||
|
||||
```ts
|
||||
export default defineRunnerConfig({
|
||||
```ts [web-ext.config.ts]
|
||||
export default defineWebExtConfig({
|
||||
binaries: {
|
||||
chrome: '/path/to/chrome-beta', // Use Chrome Beta instead of regular Chrome
|
||||
firefox: 'firefoxdeveloperedition', // Use Firefox Developer Edition instead of regular Firefox
|
||||
@@ -54,7 +54,7 @@ To persist data, set the `--user-data-dir` flag:
|
||||
:::code-group
|
||||
|
||||
```ts [Mac/Linux]
|
||||
export default defineRunnerConfig({
|
||||
export default defineWebExtConfig({
|
||||
chromiumArgs: ['--user-data-dir=./.wxt/chrome-data'],
|
||||
});
|
||||
```
|
||||
@@ -62,7 +62,7 @@ export default defineRunnerConfig({
|
||||
```ts [Windows]
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
export default defineRunnerConfig({
|
||||
export default defineWebExtConfig({
|
||||
// On Windows, the path must be absolute
|
||||
chromiumProfile: resolve('.wxt/chrome-data'),
|
||||
keepProfileChanges: true,
|
||||
@@ -81,8 +81,8 @@ You can use any directory you'd like for `--user-data-dir`, the examples above c
|
||||
|
||||
If you prefer to load the extension into your browser manually, you can disable the auto-open behavior:
|
||||
|
||||
```ts
|
||||
export default defineRunnerConfig({
|
||||
```ts [web-ext.config.ts]
|
||||
export default defineWebExtConfig({
|
||||
disabled: true,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -17,62 +17,3 @@ If you're running into errors while importing entrypoints, run `wxt prepare --de
|
||||
:::
|
||||
|
||||
Once the environment has been polyfilled and your code pre-processed, it's up the entrypoint loader to import your code, extracting the options from the default export.
|
||||
|
||||
There are two options for loading your entrypoints:
|
||||
|
||||
1. `vite-node` - default as of `v0.19.0`
|
||||
2. `jiti` (**DEPRECATED, will be removed in `v0.20.0`**) - Default before `v0.19.0`
|
||||
|
||||
## vite-node
|
||||
|
||||
Since 0.19.0, WXT uses `vite-node`, the same tool that powers Vitest and Nuxt, to import your entrypoint files. It re-uses the same vite config used when building your extension, making it the most stable entrypoint loader.
|
||||
|
||||
## jiti
|
||||
|
||||
To enable `jiti`:
|
||||
|
||||
```ts
|
||||
export default defineConfig({
|
||||
entrypointLoader: 'jiti',
|
||||
});
|
||||
```
|
||||
|
||||
This is the original method WXT used to import TS files. However, because it doesn't support vite plugins like `vite-node`, it does one additional pre-processing step: It removes **_ALL_** imports from your code.
|
||||
|
||||
That means you cannot use imported variables outside the `main` function in JS entrypoints, like for content script `matches` or other options:
|
||||
|
||||
```ts [entrypoints/content.ts]
|
||||
import { GOOGLE_MATCHES } from '~/utils/match-patterns';
|
||||
|
||||
export default defineContentScript({
|
||||
matches: GOOGLE_MATCHES,
|
||||
main() {
|
||||
// ...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```
|
||||
$ wxt build
|
||||
wxt build
|
||||
|
||||
WXT 0.14.1
|
||||
ℹ Building chrome-mv3 for production with Vite 5.0.5
|
||||
✖ Command failed after 360 ms
|
||||
|
||||
[8:55:54 AM] ERROR entrypoints/content.ts: Cannot use imported variable "GOOGLE_MATCHES" before main function.
|
||||
```
|
||||
|
||||
Usually, this error occurs when you try to extract options into a shared file or when running code outside the `main` function. To fix the example from above, use literal values when defining an entrypoint instead of importing them:
|
||||
|
||||
```ts
|
||||
import { GOOGLE_MATCHES } from '~/utils/match-patterns'; // [!code --]
|
||||
|
||||
export default defineContentScript({
|
||||
matches: GOOGLE_MATCHES, // [!code --]
|
||||
matches: ['*//*.google.com/*'], // [!code ++]
|
||||
main() {
|
||||
// ...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -42,6 +42,8 @@ WXT provides some custom environment variables based on the current command:
|
||||
| `import.meta.env.EDGE` | `boolean` | Equivalent to `import.meta.env.BROWSER === "edge"` |
|
||||
| `import.meta.env.OPERA` | `boolean` | Equivalent to `import.meta.env.BROWSER === "opera"` |
|
||||
|
||||
You can set the [`targetBrowsers`](/api/reference/wxt/interfaces/InlineConfig#targetbrowsers) option to make the `BROWSER` variable a more specific type, like `"chrome" | "firefox"`.
|
||||
|
||||
You can also access all of [Vite's environment variables](https://vite.dev/guide/env-and-mode.html#env-variables):
|
||||
|
||||
| Usage | Type | Description |
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
Define runtime configuration in a single place, `<srcDir>/app.config.ts`:
|
||||
|
||||
```ts
|
||||
import { defineAppConfig } from 'wxt/sandbox';
|
||||
import { defineAppConfig } from '#imports';
|
||||
|
||||
// Define types for your config
|
||||
declare module 'wxt/sandbox' {
|
||||
declare module 'wxt/utils/define-app-config' {
|
||||
export interface WxtAppConfig {
|
||||
theme?: 'light' | 'dark';
|
||||
}
|
||||
@@ -26,7 +26,7 @@ This file is committed to the repo, so don't put any secrets here. Instead, use
|
||||
To access runtime config, WXT provides the `useAppConfig` function:
|
||||
|
||||
```ts
|
||||
import { useAppConfig } from 'wxt/sandbox';
|
||||
import { useAppConfig } from '#imports';
|
||||
|
||||
console.log(useAppConfig()); // { theme: "dark" }
|
||||
```
|
||||
@@ -36,7 +36,7 @@ console.log(useAppConfig()); // { theme: "dark" }
|
||||
You can use environment variables in the `app.config.ts` file.
|
||||
|
||||
```ts
|
||||
declare module 'wxt/sandbox' {
|
||||
declare module 'wxt/utils/define-app-config' {
|
||||
export interface WxtAppConfig {
|
||||
apiKey?: string;
|
||||
skipWelcome: boolean;
|
||||
|
||||
@@ -256,13 +256,13 @@ export default defineContentScript({
|
||||
|
||||
:::
|
||||
|
||||
See the [API Reference](/api/reference/wxt/client/functions/createIntegratedUi) for the complete list of options.
|
||||
See the [API Reference](/api/reference/wxt/utils/content-script-ui/integrated/functions/createIntegratedUi) for the complete list of options.
|
||||
|
||||
### Shadow Root
|
||||
|
||||
Often in web extensions, you don't want your content script's CSS affecting the page, or vise-versa. The [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot) API is ideal for this.
|
||||
|
||||
WXT's [`createShadowRootUi`](/api/reference/wxt/client/functions/createShadowRootUi) abstracts all the `ShadowRoot` setup away, making it easy to create UIs whose styles are isolated from the page. It also supports an optional `isolateEvents` parameter to further isolate user interactions.
|
||||
WXT's [`createShadowRootUi`](/api/reference/wxt/utils/content-script-ui/shadow-root/functions/createShadowRootUi) abstracts all the `ShadowRoot` setup away, making it easy to create UIs whose styles are isolated from the page. It also supports an optional `isolateEvents` parameter to further isolate user interactions.
|
||||
|
||||
To use `createShadowRootUi`, follow these steps:
|
||||
|
||||
@@ -445,7 +445,7 @@ export default defineContentScript({
|
||||
|
||||
:::
|
||||
|
||||
See the [API Reference](/api/reference/wxt/client/functions/createShadowRootUi) for the complete list of options.
|
||||
See the [API Reference](/api/reference/wxt/utils/content-script-ui/shadow-root/functions/createShadowRootUi) for the complete list of options.
|
||||
|
||||
Full examples:
|
||||
|
||||
@@ -456,7 +456,7 @@ Full examples:
|
||||
|
||||
If you don't need to run your UI in the same frame as the content script, you can use an IFrame to host your UI instead. Since an IFrame just hosts an HTML page, **_HMR is supported_**.
|
||||
|
||||
WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/client/functions/createIframeUi), which simplifies setting up the IFrame.
|
||||
WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/utils/content-script-ui/iframe/functions/createIframeUi), which simplifies setting up the IFrame.
|
||||
|
||||
1. Create an HTML page that will be loaded into your IFrame:
|
||||
```html
|
||||
@@ -510,7 +510,7 @@ WXT provides a helper function, [`createIframeUi`](/api/reference/wxt/client/fun
|
||||
});
|
||||
```
|
||||
|
||||
See the [API Reference](/api/reference/wxt/client/functions/createIframeUi) for the complete list of options.
|
||||
See the [API Reference](/api/reference/wxt/utils/content-script-ui/iframe/functions/createIframeUi) for the complete list of options.
|
||||
|
||||
## Isolated World vs Main World
|
||||
|
||||
@@ -621,7 +621,7 @@ export default defineContentScript({
|
||||
When the `ui.remove` is called, `autoMount` also stops.
|
||||
:::
|
||||
|
||||
See the [API Reference](/api/reference/wxt/client/interfaces/ContentScriptUi.html#automount) for the complete list of options.
|
||||
See the [API Reference](/api/reference/wxt/utils/content-script-ui/types/interfaces/ContentScriptUi#automount) for the complete list of options.
|
||||
|
||||
## Dealing with SPAs
|
||||
|
||||
|
||||
@@ -4,50 +4,30 @@ outline: deep
|
||||
|
||||
# Entrypoints
|
||||
|
||||
WXT uses the files inside the `entrypoints/` directory as inputs when bundling your extension. They can be HTML, JS, CSS, or any variant of those file types supported by Vite (Pug, TS, JSX, SCSS, etc).
|
||||
WXT uses the files inside the `entrypoints/` directory as inputs when bundling your extension. They can be HTML, JS, CSS, or any variant of those file types supported by Vite (TS, JSX, SCSS, etc).
|
||||
|
||||
Here's an example set of entrypoints:
|
||||
## Folder Structure
|
||||
|
||||
Inside the `entrypoints/` directory, an entrypoint is defined as a single file or directory (with an `index` file) inside it.
|
||||
|
||||
:::code-group
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```html
|
||||
```html [Single File]
|
||||
📂 entrypoints/
|
||||
📂 popup/
|
||||
📄 index.html
|
||||
📄 main.ts
|
||||
📄 style.css
|
||||
📄 background.ts
|
||||
📄 content.ts
|
||||
📄 {name}.{ext}
|
||||
```
|
||||
|
||||
[[toc]]
|
||||
<!-- prettier-ignore -->
|
||||
```html [Directory]
|
||||
📂 entrypoints/
|
||||
📂 {name}/
|
||||
📄 index.{ext}
|
||||
```
|
||||
|
||||
## Listed vs Unlisted
|
||||
|
||||
For web extensions, there are two types of entrypoints:
|
||||
|
||||
- **Listed**: Referenced in the `manifest.json`
|
||||
- **Unlisted**: Not referenced in the `manifest.json`
|
||||
|
||||
Throughout the rest of WXT's documentation, listed entrypoints are referred to by name. For example:
|
||||
|
||||
- Popup
|
||||
- Options
|
||||
- Background
|
||||
- Content Scripts
|
||||
- Etc.
|
||||
|
||||
Some examples of "unlisted" entrypoints:
|
||||
|
||||
- A welcome page shown when the extension is installed
|
||||
- JS files injected by content scripts into the page's main world
|
||||
|
||||
:::tip
|
||||
Regardless of whether an entrypoint is listed or unlisted, it will still be bundled into your extension and be available at runtime.
|
||||
:::
|
||||
|
||||
## Adding Entrypoints
|
||||
|
||||
An entrypoint can be defined as a single file or directory with an `index` file inside it.
|
||||
The entrypoint's `name` dictates the type of entrypoint. For example, to add a ["Background" entrypoint](#background), either of these files would work:
|
||||
|
||||
:::code-group
|
||||
|
||||
@@ -66,10 +46,96 @@ An entrypoint can be defined as a single file or directory with an `index` file
|
||||
|
||||
:::
|
||||
|
||||
The entrypoint's name dictates the type of entrypoint, listed vs unlisted. In this example, "background" is the name of the ["Background" entrypoint](#background).
|
||||
|
||||
Refer to the [Entrypoint Types](#entrypoint-types) section for the full list of listed entrypoints and their filename patterns.
|
||||
|
||||
### Including Other Files
|
||||
|
||||
When using an entrypoint directory, `entrypoints/{name}/index.{ext}`, you can add related files next to the `index` file.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```html
|
||||
📂 entrypoints/
|
||||
📂 popup/
|
||||
📄 index.html ← This file is the entrypoint
|
||||
📄 main.ts
|
||||
📄 style.css
|
||||
📂 background/
|
||||
📄 index.ts ← This file is the entrypoint
|
||||
📄 alarms.ts
|
||||
📄 messaging.ts
|
||||
📂 youtube.content/
|
||||
📄 index.ts ← This file is the entrypoint
|
||||
📄 style.css
|
||||
```
|
||||
|
||||
:::danger
|
||||
**DO NOT** put files related to an entrypoint directly inside the `entrypoints/` directory. WXT will treat them as entrypoints and try to build them, usually resulting in an error.
|
||||
|
||||
Instead, use a directory for that entrypoint:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```html
|
||||
📂 entrypoints/
|
||||
📄 popup.html <!-- [!code --] -->
|
||||
📄 popup.ts <!-- [!code --] -->
|
||||
📄 popup.css <!-- [!code --] -->
|
||||
📂 popup/ <!-- [!code ++] -->
|
||||
📄 index.html <!-- [!code ++] -->
|
||||
📄 main.ts <!-- [!code ++] -->
|
||||
📄 style.css <!-- [!code ++] -->
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Deeply Nested Entrypoints
|
||||
|
||||
While the `entrypoints/` directory might resemble the `pages/` directory of other web frameworks, like Nuxt or Next.js, **it does not support deeply nesting entrypoints** in the same way.
|
||||
|
||||
Entrypoints must be zero or one levels deep for WXT to discover and build them:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```html
|
||||
📂 entrypoints/
|
||||
📂 youtube/ <!-- [!code --] -->
|
||||
📂 content/ <!-- [!code --] -->
|
||||
📄 index.ts <!-- [!code --] -->
|
||||
📄 ... <!-- [!code --] -->
|
||||
📂 injected/ <!-- [!code --] -->
|
||||
📄 index.ts <!-- [!code --] -->
|
||||
📄 ... <!-- [!code --] -->
|
||||
📂 youtube.content/ <!-- [!code ++] -->
|
||||
📄 index.ts <!-- [!code ++] -->
|
||||
📄 ... <!-- [!code ++] -->
|
||||
📂 youtube-injected/ <!-- [!code ++] -->
|
||||
📄 index.ts <!-- [!code ++] -->
|
||||
📄 ... <!-- [!code ++] -->
|
||||
```
|
||||
|
||||
## Unlisted Entrypoints
|
||||
|
||||
In web extensions, there are two types of entrypoints:
|
||||
|
||||
1. **Listed**: Referenced in the `manifest.json`
|
||||
2. **Unlisted**: Not referenced in the `manifest.json`
|
||||
|
||||
Throughout the rest of WXT's documentation, listed entrypoints are referred to by name. For example:
|
||||
|
||||
- Popup
|
||||
- Options
|
||||
- Background
|
||||
- Content Script
|
||||
|
||||
However, not all entrypoints in web extensions are listed in the manifest. Some are not listed in the manifest, but are still used by extensions. For example:
|
||||
|
||||
- A welcome page shown in a new tab when the extension is installed
|
||||
- JS files injected by content scripts into the main world
|
||||
|
||||
For more details on how to add unlisted entrypoints, see:
|
||||
|
||||
- [Unlisted Pages](#unlisted-pages)
|
||||
- [Unlisted Scripts](#unlisted-scripts)
|
||||
- [Unlisted CSS](#unlisted-css)
|
||||
|
||||
## Defining Manifest Options
|
||||
|
||||
Most listed entrypoints have options that need to be added to the `manifest.json`. However with WXT, instead of defining the options in a separate file, _you define these options inside the entrypoint file itself_.
|
||||
@@ -106,8 +172,6 @@ When building your extension, WXT will look at the options defined in your entry
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/background/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background)
|
||||
|
||||
For MV2, the background is added as a script to the background page. For MV3, the background becomes a service worker.
|
||||
|
||||
<EntrypointPatterns
|
||||
:patterns="[
|
||||
['background.[jt]s', 'background.js'],
|
||||
@@ -141,6 +205,25 @@ export default defineBackground({
|
||||
|
||||
:::
|
||||
|
||||
For MV2, the background is added as a script to the background page. For MV3, the background becomes a service worker.
|
||||
|
||||
When defining your background entrypoint, keep in mind that WXT will import this file in a NodeJS environment during the build process. That means you cannot place any runtime code outside the `main` function.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
browser.action.onClick.addListener(() => { // [!code --]
|
||||
// ... // [!code --]
|
||||
}); // [!code --]
|
||||
|
||||
export default defineBackground(() => {
|
||||
browser.action.onClick.addListener(() => { // [!code ++]
|
||||
// ... // [!code ++]
|
||||
}); // [!code ++]
|
||||
});
|
||||
```
|
||||
|
||||
Refer to the [Entrypoint Loaders](/guide/essentials/config/entrypoint-loaders) documentation for more details.
|
||||
|
||||
### Bookmarks
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
|
||||
@@ -169,18 +252,18 @@ export default defineBackground({
|
||||
</html>
|
||||
```
|
||||
|
||||
When you define a Bookmarks entrypoint, WXT will automatically update the manifest to override the browser's bookmarks page with your own HTML page.
|
||||
|
||||
### Content Scripts
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/content_scripts/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts)
|
||||
|
||||
See [Content Script UI](/guide/essentials/content-scripts) for more info on creating UIs and including CSS in content scripts.
|
||||
|
||||
<EntrypointPatterns
|
||||
:patterns="[
|
||||
['content.[jt]sx?', 'content-scripts/content.js'],
|
||||
['content/index.[jt]sx?', 'content-scripts/content.js'],
|
||||
['<name>.content.[jt]sx?', 'content-scripts/<name>.js'],
|
||||
['<name>.content/index.[jt]sx?', 'content-scripts/<name>.js'],
|
||||
['{name}.content.[jt]sx?', 'content-scripts/{name}.js'],
|
||||
['{name}.content/index.[jt]sx?', 'content-scripts/{name}.js'],
|
||||
]"
|
||||
/>
|
||||
|
||||
@@ -213,12 +296,29 @@ export default defineContentScript({
|
||||
});
|
||||
```
|
||||
|
||||
When defining content script entrypoints, keep in mind that WXT will import this file in a NodeJS environment during the build process. That means you cannot place any runtime code outside the `main` function.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
browser.runtime.onMessage.addListener((message) => { // [!code --]
|
||||
// ... // [!code --]
|
||||
}); // [!code --]
|
||||
|
||||
export default defineBackground(() => {
|
||||
browser.runtime.onMessage.addListener((message) => { // [!code ++]
|
||||
// ... // [!code ++]
|
||||
}); // [!code ++]
|
||||
});
|
||||
```
|
||||
|
||||
Refer to the [Entrypoint Loaders](/guide/essentials/config/entrypoint-loaders) documentation for more details.
|
||||
|
||||
See [Content Script UI](/guide/essentials/content-scripts) for more info on creating UIs and including CSS in content scripts.
|
||||
|
||||
### Devtools
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/devtools/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/devtools_page)
|
||||
|
||||
Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/examples/devtools-extension#readme) to add different panels and panes.
|
||||
|
||||
<EntrypointPatterns
|
||||
:patterns="[
|
||||
['devtools.html', 'devtools.html'],
|
||||
@@ -242,6 +342,8 @@ Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/exam
|
||||
</html>
|
||||
```
|
||||
|
||||
Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/examples/devtools-extension#readme) to add different panels and panes.
|
||||
|
||||
### History
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
|
||||
@@ -270,6 +372,8 @@ Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/exam
|
||||
</html>
|
||||
```
|
||||
|
||||
When you define a History entrypoint, WXT will automatically update the manifest to override the browser's history page with your own HTML page.
|
||||
|
||||
### Newtab
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/override/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/chrome_url_overrides)
|
||||
@@ -298,6 +402,8 @@ Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/exam
|
||||
</html>
|
||||
```
|
||||
|
||||
When you define a Newtab entrypoint, WXT will automatically update the manifest to override the browser's new tab page with your own HTML page.
|
||||
|
||||
### Options
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/options/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/options_ui)
|
||||
@@ -387,8 +493,8 @@ Firefox does not support sandboxed pages.
|
||||
:patterns="[
|
||||
['sandbox.html', 'sandbox.html'],
|
||||
['sandbox/index.html', 'sandbox.html'],
|
||||
['<name>.sandbox.html', '<name>.html'],
|
||||
['<name>.sandbox/index.html', '<name>.html'],
|
||||
['{name}.sandbox.html', '{name}.html'],
|
||||
['{name}.sandbox/index.html', '{name}.html'],
|
||||
]"
|
||||
/>
|
||||
|
||||
@@ -414,14 +520,12 @@ Firefox does not support sandboxed pages.
|
||||
|
||||
[Chrome Docs](https://developer.chrome.com/docs/extensions/reference/sidePanel/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Sidebars)
|
||||
|
||||
In Chrome, side panels use the `side_panel` API, while Firefox uses the `sidebar_action` API.
|
||||
|
||||
<EntrypointPatterns
|
||||
:patterns="[
|
||||
['sidepanel.html', 'sidepanel.html'],
|
||||
['sidepanel/index.html', 'sidepanel.html'],
|
||||
['<name>.sidepanel.html', '<name>.html` '],
|
||||
['<name>.sidepanel/index.html', '<name>.html` '],
|
||||
['{name}.sidepanel.html', '{name}.html` '],
|
||||
['{name}.sidepanel/index.html', '{name}.html` '],
|
||||
]"
|
||||
/>
|
||||
|
||||
@@ -455,20 +559,18 @@ In Chrome, side panels use the `side_panel` API, while Firefox uses the `sidebar
|
||||
</html>
|
||||
```
|
||||
|
||||
In Chrome, side panels use the `side_panel` API, while Firefox uses the `sidebar_action` API.
|
||||
|
||||
### Unlisted CSS
|
||||
|
||||
Follow Vite's guide to setup your preprocessor of choice: https://vitejs.dev/guide/features.html#css-pre-processors
|
||||
|
||||
CSS entrypoints are always unlisted. To add CSS to a content script, see the [Content Script](/guide/essentials/content-scripts#css) docs.
|
||||
|
||||
<EntrypointPatterns
|
||||
:patterns="[
|
||||
['<name>.(css|scss|sass|less|styl|stylus)', '<name>.css'],
|
||||
['<name>/index.(css|scss|sass|less|styl|stylus)', '<name>.css'],
|
||||
['{name}.(css|scss|sass|less|styl|stylus)', '{name}.css'],
|
||||
['{name}/index.(css|scss|sass|less|styl|stylus)', '{name}.css'],
|
||||
['content.(css|scss|sass|less|styl|stylus)', 'content-scripts/content.css'],
|
||||
['content/index.(css|scss|sass|less|styl|stylus)', 'content-scripts/content.css'],
|
||||
['<name>.content.(css|scss|sass|less|styl|stylus)', 'content-scripts/<name>.css'],
|
||||
['<name>.content/index.(css|scss|sass|less|styl|stylus)', 'content-scripts/<name>.css'],
|
||||
['{name}.content.(css|scss|sass|less|styl|stylus)', 'content-scripts/{name}.css'],
|
||||
['{name}.content/index.(css|scss|sass|less|styl|stylus)', 'content-scripts/{name}.css'],
|
||||
]"
|
||||
/>
|
||||
|
||||
@@ -478,12 +580,16 @@ body {
|
||||
}
|
||||
```
|
||||
|
||||
Follow Vite's guide to setup your preprocessor of choice: https://vitejs.dev/guide/features.html#css-pre-processors
|
||||
|
||||
CSS entrypoints are always unlisted. To add CSS to a content script, see the [Content Script](/guide/essentials/content-scripts#css) docs.
|
||||
|
||||
### Unlisted Pages
|
||||
|
||||
<EntrypointPatterns
|
||||
:patterns="[
|
||||
['<name>.html', '<name>.html'],
|
||||
['<name>/index.html', '<name>.html'],
|
||||
['{name}.html', '{name}.html'],
|
||||
['{name}/index.html', '{name}.html'],
|
||||
]"
|
||||
/>
|
||||
|
||||
@@ -505,20 +611,21 @@ body {
|
||||
</html>
|
||||
```
|
||||
|
||||
Pages are accessible at `/<name>.html`:
|
||||
At runtime, unlisted pages are accessible at `/{name}.html`:
|
||||
|
||||
```ts
|
||||
const url = browser.runtime.getURL('/<name>.html');
|
||||
const url = browser.runtime.getURL('/{name}.html');
|
||||
|
||||
console.log(url); // "chrome-extension://<id>/<name>.html"
|
||||
console.log(url); // "chrome-extension://{id}/{name}.html"
|
||||
window.open(url); // Open the page in a new tab
|
||||
```
|
||||
|
||||
### Unlisted Scripts
|
||||
|
||||
<EntrypointPatterns
|
||||
:patterns="[
|
||||
['<name>.[jt]sx?', '<name>.js'],
|
||||
['<name>/index.[jt]sx?', '<name>.js'],
|
||||
['{name}.[jt]sx?', '{name}.js'],
|
||||
['{name}/index.[jt]sx?', '{name}.js'],
|
||||
]"
|
||||
/>
|
||||
|
||||
@@ -544,12 +651,29 @@ export default defineUnlistedScript({
|
||||
|
||||
:::
|
||||
|
||||
Scripts are accessible from `/<name>.js`:
|
||||
At runtime, unlisted scripts are accessible from `/{name}.js`:
|
||||
|
||||
```ts
|
||||
const url = browser.runtime.getURL('/<name>.js');
|
||||
const url = browser.runtime.getURL('/{name}.js');
|
||||
|
||||
console.log(url); // "chrome-extension://<id>/<name>.js"
|
||||
console.log(url); // "chrome-extension://{id}/{name}.js"
|
||||
```
|
||||
|
||||
You are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related assets to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
|
||||
|
||||
When defining an unlisted script, keep in mind that WXT will import this file in a NodeJS environment during the build process. That means you cannot place any runtime code outside the `main` function.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
document.querySelectorAll('a').forEach((anchor) => { // [!code --]
|
||||
// ... // [!code --]
|
||||
}); // [!code --]
|
||||
|
||||
export default defineUnlistedScript(() => {
|
||||
document.querySelectorAll('a').forEach((anchor) => { // [!code ++]
|
||||
// ... // [!code ++]
|
||||
}); // [!code ++]
|
||||
});
|
||||
```
|
||||
|
||||
Refer to the [Entrypoint Loaders](/guide/essentials/config/entrypoint-loaders) documentation for more details.
|
||||
|
||||
@@ -4,61 +4,47 @@
|
||||
|
||||
Different browsers provide different global variables for accessing the extension APIs (chrome provides `chrome`, firefox provides `browser`, etc).
|
||||
|
||||
WXT simplifies this - always use `browser`:
|
||||
WXT merges these two into a unified API accessed through the `browser` variable.
|
||||
|
||||
```ts
|
||||
import { browser } from 'wxt/browser';
|
||||
|
||||
browser.action.onClicked.addListener(() => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Other than that, refer to Chrome and Mozilla's documentation for how to use specific APIs. Everything a normal extension can do, WXT can do as well, just via `browser` instead of `chrome`.
|
||||
:::tip
|
||||
With auto-imports enabled, you don't even need to import this variable from `wxt/browser`!
|
||||
:::
|
||||
|
||||
## Webextension Polyfill
|
||||
|
||||
> Since `v0.1.0`
|
||||
|
||||
By default, WXT uses the [`webextension-polyfill` by Mozilla](https://www.npmjs.com/package/webextension-polyfill) to make the extension API consistent between browsers.
|
||||
|
||||
To access types, you should import the relevant namespace from `wxt/browser`:
|
||||
|
||||
```ts
|
||||
import { Runtime } from 'wxt/browser';
|
||||
|
||||
function handleMessage(message: any, sender: Runtime.Sender) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Disabling the polyfill
|
||||
|
||||
> Since `v0.19.0`
|
||||
|
||||
After the release of MV3 and Chrome's official deprecation of MV2 in June 2024, the polyfill isn't really doing anything useful anymore.
|
||||
|
||||
You can disable it with a single line:
|
||||
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
```
|
||||
|
||||
This will change `wxt/browser` to simply export the `browser` or `chrome` globals based on browser at runtime:
|
||||
The `browser` variable WXT provides is a simple export of the `browser` or `chrome` globals provided by the browser at runtime:
|
||||
|
||||
<<< @/../packages/browser/src/index.mjs#snippet
|
||||
|
||||
Accessing types is a little different with the polyfill disabled. They do not need to be imported; they're available on the `browser` object itself:
|
||||
This means you can use the promise-style API for both MV2 and MV3, and it will work across all browsers (Chromium, Firefox, Safari, etc).
|
||||
|
||||
## Accessing Types
|
||||
|
||||
All types can be accessed via WXT's `Browser` namespace:
|
||||
|
||||
```ts
|
||||
function handleMessage(message: any, sender: browser.runtime.Sender) {
|
||||
import { type Browser } from 'wxt/browser';
|
||||
|
||||
function handleMessage(message: any, sender: Browser.runtime.MessageSender) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Using `webextension-polyfill`
|
||||
|
||||
If you want to use the `webextension-polyfill` when importing `browser`, you can do so by installing the `@wxt-dev/webextension-polyfill` package.
|
||||
|
||||
See it's [Installation Guide](https://github.com/wxt-dev/wxt/blob/main/packages/webextension-polyfill/README.md) to get started.
|
||||
|
||||
## Feature Detection
|
||||
|
||||
Depending on the manifest version and browser, some APIs are not available at runtime. If an API is not available, it will be `undefined`.
|
||||
Depending on the manifest version, browser, and permissions, some APIs are not available at runtime. If an API is not available, it will be `undefined`.
|
||||
|
||||
:::warning
|
||||
Types will not help you here. The types WXT provides for `browser` assume all APIs exist. You are responsible for knowing whether an API is available or not.
|
||||
|
||||
@@ -30,9 +30,10 @@ Here's a brief summary of each of these files and directories:
|
||||
- `.wxt/`: Generated by WXT, it contains TS config
|
||||
- `assets/`: Contains all CSS, images, and other assets that should be processed by WXT
|
||||
- `components/`: Auto-imported by default, contains UI components
|
||||
- `composables/`: Auto-imported by default, contains composable functions for Vue
|
||||
- `composables/`: Auto-imported by default, contains source code for your project's composable functions for Vue
|
||||
- `entrypoints/`: Contains all the entrypoints that get bundled into your extension
|
||||
- `hooks/`: Auto-imported by default, contains hooks for React and Solid
|
||||
- `hooks/`: Auto-imported by default, contains source code for your project's hooks for React and Solid
|
||||
- `modules/`: Contains [local WXT Modules](/guide/essentials/wxt-modules) for your project
|
||||
- `public/`: Contains any files you want to copy into the output folder as-is, without being processed by WXT
|
||||
- `utils/`: Auto-imported by default, contains generic utilities used throughout your project
|
||||
- `.env`: Contains [Environment Variables](/guide/essentials/config/environment-variables)
|
||||
@@ -60,14 +61,14 @@ After enabling it, your project structure should look like this:
|
||||
📂 {rootDir}/
|
||||
📁 .output/
|
||||
📁 .wxt/
|
||||
📁 modules/
|
||||
📁 public/
|
||||
📂 src/
|
||||
📁 assets/
|
||||
📁 components/
|
||||
📁 composables/
|
||||
📁 entrypoints/
|
||||
📁 hooks/
|
||||
📁 modules/
|
||||
📁 public/
|
||||
📁 utils/
|
||||
📄 app.config.ts
|
||||
📄 .env
|
||||
@@ -87,12 +88,12 @@ You can configure the following directories:
|
||||
export default defineConfig({
|
||||
// Relative to project root
|
||||
srcDir: "src", // default: "."
|
||||
modulesDir: "wxt-modules", // default: "modules"
|
||||
outDir: "dist", // default: ".output"
|
||||
publicDir: "static", // default: "public"
|
||||
|
||||
// Relative to srcDir
|
||||
entrypointsDir: "entries", // default: "entrypoints"
|
||||
modulesDir: "wxt-modules", // default: "modules"
|
||||
publicDir: "static", // default: "public"
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ WXT provides two commands to help automate submitting a new version for review a
|
||||
- `wxt submit init`: Setup all the required secrets and options for the `wxt submit` command
|
||||
- `wxt submit`: Submit new versions of your extension for review (and publish them automatically once approved)
|
||||
|
||||
To get started, run `wxt submit init` and follow the prompts. Once finished, you should have a `.env.submit` file! WXT will use this file to submit your updates.
|
||||
To get started, run `wxt submit init` and follow the prompts, or run `wxt submit --help` to view all available options. Once finished, you should have a `.env.submit` file! WXT will use this file to submit your updates.
|
||||
|
||||
> In CI, make sure you add all the environment variables to the submit step.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ You can use the vanilla APIs (see docs above), use [WXT's built-in storage API](
|
||||
|
||||
## Alternatives
|
||||
|
||||
1. [`wxt/storage`](/storage) (recommended): WXT ships with its own wrapper around the vanilla storage APIs that simplifies common use cases
|
||||
1. [`wxt/utils/storage`](/storage) (recommended): WXT ships with its own wrapper around the vanilla storage APIs that simplifies common use cases
|
||||
|
||||
2. DIY: If you're migrating to WXT and already have a storage wrapper, keep using it. In the future, if you want to delete that code, you can use one of these alternatives, but there's no reason to replace working code during a migration.
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Here are real projects with unit testing setup. Look at the code and tests to se
|
||||
|
||||
### Example Tests
|
||||
|
||||
This example demonstrates that you don't have to mock `browser.storage` (used by `wxt/storage`) in tests - [`@webext-core/fake-browser`](https://webext-core.aklinker1.io/fake-browser/installation) implements storage in-memory so it behaves like it would in a real extension!
|
||||
This example demonstrates that you don't have to mock `browser.storage` (used by `wxt/utils/storage`) in tests - [`@webext-core/fake-browser`](https://webext-core.aklinker1.io/fake-browser/installation) implements storage in-memory so it behaves like it would in a real extension!
|
||||
|
||||
```ts
|
||||
import { describe, it, expect } from 'vitest';
|
||||
@@ -71,6 +71,34 @@ describe('isLoggedIn', () => {
|
||||
});
|
||||
```
|
||||
|
||||
### Mocking WXT APIs
|
||||
|
||||
First, you need to understand how the `#imports` module works. When WXT (and vitest) sees this import during a preprocessing step, the import is replaced with multiple imports pointing to their "real" import path.
|
||||
|
||||
For example, this is what your write in your source code:
|
||||
|
||||
```ts
|
||||
// What you write
|
||||
import { injectScript, createShadowRootUi } from '#imports';
|
||||
```
|
||||
|
||||
But Vitest sees this:
|
||||
|
||||
```ts
|
||||
import { injectScript } from 'wxt/browser';
|
||||
import { createShadowRootUi } from 'wxt/utils/content-script-ui/shadow-root';
|
||||
```
|
||||
|
||||
So in this case, if you wanted to mock `injectScript`, you need to pass in `"wxt/utils/inject-script"`, not `"#imports"`.
|
||||
|
||||
```ts
|
||||
vi.mock("wxt/utils/inject-script", () => ({
|
||||
injectScript: ...
|
||||
}))
|
||||
```
|
||||
|
||||
Refer to your project's `.wxt/types/imports-module.d.ts` file to lookup real import paths for `#imports`. If the file doesn't exist, run [`wxt prepare`](/guide/essentials/config/typescript).
|
||||
|
||||
## Other Testing Frameworks
|
||||
|
||||
To use a different framework, you will likely have to disable auto-imports, setup import aliases, manually mock the extension APIs, and setup the test environment to support all of WXT's features that you use.
|
||||
|
||||
@@ -17,7 +17,7 @@ There are two ways to add a module to your project:
|
||||
> Searching for ["wxt module"](https://www.npmjs.com/search?q=wxt%20module) on NPM is a good way to find published WXT modules.
|
||||
2. **Local**: add a file to your project's `modules/` directory:
|
||||
```
|
||||
<srcDir>/
|
||||
<rootDir>/
|
||||
modules/
|
||||
my-module.ts
|
||||
```
|
||||
@@ -111,12 +111,12 @@ export default defineWxtModule<AnalyticModuleOptions>({
|
||||
|
||||
```ts
|
||||
import { defineWxtModule } from 'wxt/modules';
|
||||
import 'wxt/sandbox';
|
||||
import 'wxt/utils/define-app-config';
|
||||
|
||||
export interface MyModuleRuntimeOptions {
|
||||
// Add your runtime options here...
|
||||
}
|
||||
declare module 'wxt/sandbox' {
|
||||
declare module 'wxt/utils/define-app-config' {
|
||||
export interface WxtAppConfig {
|
||||
myModule: MyModuleOptions;
|
||||
}
|
||||
|
||||
@@ -174,3 +174,16 @@ Additionally, if you want to train your own model or provide context to your edi
|
||||
https://wxt.dev/knowledge/index.json
|
||||
|
||||
You don't need to crawl the entire website, these files already contain all the relevant docs for training a LLM on WXT. But feel free to crawl it and generate your own files if you want!
|
||||
|
||||
## How do I run my WXT project with docker / [devcontainers](https://containers.dev)?
|
||||
|
||||
To run the WXT dev server in a devcontainer, but load the dev build of your extension in your browser:
|
||||
|
||||
1. **Bind-mount your project directory to your host**
|
||||
If you're using VS Code, you can open your project folder with the `Dev Containers: Open Folder in Container...` command. This keeps the folder synchronized between your host and the devcontainer, ensuring that the extension `dist` directory remains accessible from the host.
|
||||
|
||||
2. **Disable auto-opening the browser**
|
||||
WXT automatically opens your browser during development, but since you're running inside a container, it won't be able to access it. Follow the instructions [here](https://wxt.dev/guide/essentials/config/browser-startup.html#disable-opening-browser) to disable browser auto-opening in your `wxt.config.ts`.
|
||||
|
||||
3. **Tell WXT to listen on all network interfaces**
|
||||
To enable hot-reloading, your extension has to connect to the WXT dev server running inside your container. WXT will only listen on `localhost` by default, which prevents connections from outside the devcontainer. To fix this you can instruct WXT to listen on all interfaces with `wxt --host 0.0.0.0`.
|
||||
|
||||
@@ -1,17 +1,275 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Upgrading WXT
|
||||
|
||||
## Overview
|
||||
|
||||
To upgrade WXT to the latest version... just install it!
|
||||
To upgrade WXT to the latest major version:
|
||||
|
||||
1. Install it, skipping scripts so `wxt prepare` doesn't run - it will probably throw an error after a major version change (we'll run it later).
|
||||
```sh
|
||||
pnpm i wxt@latest --ignore-scripts
|
||||
```
|
||||
2. Follow the upgrade steps below to fix any breaking changes.
|
||||
3. Run `wxt prepare`. It should succeed and type errors will go away afterwords.
|
||||
```sh
|
||||
pnpm wxt prepare
|
||||
```
|
||||
4. Manually test to make sure both dev mode and production builds work.
|
||||
|
||||
For minor or patch version updates, there are no special steps. Just update it with your package manager:
|
||||
|
||||
```sh
|
||||
pnpm i wxt@latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Listed below are all the breaking changes you should address when upgrading to a new version of WXT.
|
||||
|
||||
Currently, WXT is in pre-release. This means changes to the second digit, `v0.X`, are considered major and have breaking changes. Once v1 is released, only major version bumps will have breaking changes.
|
||||
|
||||
## v0.19.0 → v0.20.0
|
||||
|
||||
v0.20 is a big release! There are lots of breaking changes because this version is intended to be a release candidate for v1.0. If all goes well, v1.0 will be released with no additional breaking changes.
|
||||
|
||||
:::tip
|
||||
Read through all the changes once before updating your code.
|
||||
:::
|
||||
|
||||
### `webextension-polyfill` Removed
|
||||
|
||||
WXT's `browser` no longer uses the `webextension-polyfill`!
|
||||
|
||||
:::details Why?
|
||||
See https://github.com/wxt-dev/wxt/issues/784
|
||||
:::
|
||||
|
||||
To upgrade, you have two options:
|
||||
|
||||
1. **Stop using the polyfill** - No changes necessary, though you may want to do some manual testing to make sure everything continues to work. None of the early testers of this feature reported any runtime issues once they stopped using the polyfill.
|
||||
- If you're already using `extensionApi: "chrome"`, then you don't need to test anything! You're already using the same `browser` object v0.20 provides by default.
|
||||
2. **Continue using the polyfill** - If you want to keep using the polyfill, you can! One less thing to worry about during this upgrade.
|
||||
- Install `webextension-polyfill` and WXT's [new polyfill module](https://www.npmjs.com/package/@wxt-dev/webextension-polyfill):
|
||||
```sh
|
||||
pnpm i webextension-polyfill @wxt-dev/webextension-polyfill
|
||||
```
|
||||
- Add the WXT module to your config:
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
modules: ['@wxt-dev/webextension-polyfill'],
|
||||
});
|
||||
```
|
||||
|
||||
The new `browser` object (and types) is backed by WXT's new package: [`@wxt-dev/browser`](https://www.npmjs.com/package/@wxt-dev/browser). This package continues WXT's mission of providing useful packages for the whole community. Just like [`@wxt-dev/storage`](https://www.npmjs.com/package/@wxt-dev/storage), [`@wxt-dev/i18n`](https://www.npmjs.com/package/@wxt-dev/i18n), [`@wxt-dev/analytics`](https://www.npmjs.com/package/@wxt-dev/analytics), it is designed to be easy to use in any web extension project, not just those using WXT, and provides a consistent API across all browsers and manifest versions.
|
||||
|
||||
### `extensionApi` Config Removed
|
||||
|
||||
The `extensionApi` config has been removed. Before, this config provided a way to opt into using the new `browser` object prior to v0.20.0.
|
||||
|
||||
Remove it from your `wxt.config.ts` file if present:
|
||||
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
extensionApi: 'chrome', // [!code --]
|
||||
});
|
||||
```
|
||||
|
||||
### Extension API Type Changes
|
||||
|
||||
With the new `browser` introduced in v0.20, how you access types has changed. WXT now provides types based on `@types/chrome` instead of `@types/webextension-polyfill`.
|
||||
|
||||
These types are more up-to-date with MV3 APIs, contain less bugs, are better organized, and don't have any auto-generated names.
|
||||
|
||||
To access types, use the new `Browser` namespace from `wxt/browser`:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
import type { Runtime } from 'wxt/browser'; // [!code --]
|
||||
import type { Browser } from 'wxt/browser'; // [!code ++]
|
||||
|
||||
function getMessageSenderUrl(sender: Runtime.MessageSender): string { // [!code --]
|
||||
function getMessageSenderUrl(sender: Browser.runtime.MessageSender): string { // [!code ++]
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
> If you use auto-imports, `Browser` will be available without manually importing it.
|
||||
|
||||
Not all type names will be the same as what `@types/webextension-polyfill` provides. You'll have to find the new type names by looking at the types of the `browser.*` API's you use.
|
||||
|
||||
### `public/` and `modules/` Directories Moved
|
||||
|
||||
The default location for the `public/` and `modules/` directories have changed to better align with standards set by other frameworks (Nuxt, Next, Astro, etc). Now, each path is relative to the project's **root directory**, not the src directory.
|
||||
|
||||
- If you follow the default folder structure, you don't need to make any changes.
|
||||
- If you set a custom `srcDir`, you have two options:
|
||||
1. Move the your `public/` and `modules/` directories to the project root:
|
||||
<!-- prettier-ignore -->
|
||||
```html
|
||||
📂 {rootDir}/
|
||||
📁 modules/ <!-- [!code ++] -->
|
||||
📁 public/ <!-- [!code ++] -->
|
||||
📂 src/
|
||||
📁 components/
|
||||
📁 entrypoints/
|
||||
📁 modules/ <!-- [!code --] -->
|
||||
📁 public/ <!-- [!code --] -->
|
||||
📁 utils/
|
||||
📄 app.config.ts
|
||||
📄 wxt.config.ts
|
||||
```
|
||||
2. Keep the folders in the same place and update your project config:
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
srcDir: 'src',
|
||||
publicDir: 'src/public', // [!code ++]
|
||||
modulesDir: 'src/modules', // [!code ++]
|
||||
});
|
||||
```
|
||||
|
||||
### Import Path Changes and `#imports`
|
||||
|
||||
The APIs exported by `wxt/sandbox`, `wxt/client`, or `wxt/storage` have moved to individual exports under the `wxt/utils/*` path.
|
||||
|
||||
:::details Why?
|
||||
As WXT grows and more utilities are added, any helper with side-effects will not be tree-shaken out of your final bundle.
|
||||
|
||||
This can cause problems because not every API used by these side-effects is available in every type of entrypoint. Some APIs can only be used in the background, sandboxed pages can't use any extension API, etc. This was leading to JS throwing errors in the top-level scope, preventing your code from running.
|
||||
|
||||
Splitting each util into it's own module solves this problem, making sure you're only importing APIs and side-effects into entrypoints they can run in.
|
||||
:::
|
||||
|
||||
Refer to the updated [API Reference](/api/reference/) to see the list of new import paths.
|
||||
|
||||
However, you don't need to memorize or learn the new import paths! v0.20 introduces a new virtual module, `#imports`, that abstracts all this away from developers. See the [blog post](/blog/2024-12-06-using-imports-module) for more details about how this module works.
|
||||
|
||||
So to upgrade, just replace any imports from `wxt/storage`, `wxt/client`, and `wxt/sandbox` with an import to the new `#imports` module:
|
||||
|
||||
```ts
|
||||
import { storage } from 'wxt/storage'; // [!code --]
|
||||
import { defineContentScript } from 'wxt/sandbox'; // [!code --]
|
||||
import { ContentScriptContext, useAppConfig } from 'wxt/client'; // [!code --]
|
||||
import { storage } from '#imports'; // [!code ++]
|
||||
import { defineContentScript } from '#imports'; // [!code ++]
|
||||
import { ContentScriptContext, useAppConfig } from '#imports'; // [!code ++]
|
||||
```
|
||||
|
||||
You can combine the imports into a single import statement, but it's easier to just find/replace each statement.
|
||||
|
||||
```ts
|
||||
import { storage } from 'wxt/storage'; // [!code --]
|
||||
import { defineContentScript } from 'wxt/sandbox'; // [!code --]
|
||||
import { ContentScriptContext, useAppConfig } from 'wxt/client'; // [!code --]
|
||||
import {
|
||||
// [!code ++]
|
||||
storage, // [!code ++]
|
||||
defineContentScript, // [!code ++]
|
||||
ContentScriptContext, // [!code ++]
|
||||
useAppConfig, // [!code ++]
|
||||
} from '#imports'; // [!code ++]
|
||||
```
|
||||
|
||||
:::tip
|
||||
Before types will work, you'll need to run `wxt prepare` after installing v0.20 to generate the new TypeScript declarations.
|
||||
:::
|
||||
|
||||
### `createShadowRootUi` CSS Changes
|
||||
|
||||
WXT now resets styles inherited from the webpage (`visibility`, `color`, `font-size`, etc.) by setting `all: initial` inside the shadow root.
|
||||
|
||||
:::warning
|
||||
This doesn't effect `rem` units. You should continue using `postcss-rem-to-px` or an equivalent library if the webpage sets the HTML element's `font-size`.
|
||||
:::
|
||||
|
||||
If you use `createShadowRootUi`:
|
||||
|
||||
1. Remove any manual CSS overrides that reset the style of specific websites. For example:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```css [entrypoints/reddit.content/style.css]
|
||||
body { /* [!code --] */
|
||||
/* Override Reddit's default "hidden" visibility on elements */ /* [!code --] */
|
||||
visibility: visible !important; /* [!code --] */
|
||||
} /* [!code --] */
|
||||
```
|
||||
|
||||
2. Double check that your UI looks the same as before.
|
||||
|
||||
If you run into problems with the new behavior, you can disable it and continue using your current CSS:
|
||||
|
||||
```ts
|
||||
const ui = await createShadowRootUi({
|
||||
inheritStyles: true, // [!code ++]
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Default Output Directories Changed
|
||||
|
||||
The default value for the [`outDirTemplate`](/api/reference/wxt/interfaces/InlineConfig#outdirtemplate) config has changed. Now, different build modes are output to different directories:
|
||||
|
||||
- `--mode production` → `.output/chrome-mv3`: Production builds are unchanged
|
||||
- `--mode development` → `.output/chrome-mv3-dev`: Dev mode now has a `-dev` suffix so it doesn't overwrite production builds
|
||||
- `--mode custom` → `.output/chrome-mv3-custom`: Other custom modes end with a `-[mode]` suffix
|
||||
|
||||
To use the old behavior, writing all output to the same directory, set the `outDirTemplate` option:
|
||||
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
outDirTemplate: '{{browser}}-mv{{manifestVersion}}', // [!code ++]
|
||||
});
|
||||
```
|
||||
|
||||
:::warning
|
||||
If you've previously loaded the extension into your browser manually for development, you'll need to uninstall and re-install it from the new dev output directory.
|
||||
:::
|
||||
|
||||
### Deprecated APIs Removed
|
||||
|
||||
- `entrypointLoader` option: WXT now uses `vite-node` for importing entrypoints during the build process.
|
||||
> This was deprecated in v0.19.0, see the [v0.19 section](#v0-18-5-rarr-v0-19-0) for migration steps.
|
||||
- `transformManifest` option: Use the `build:manifestGenerated` hook to transform the manifest instead:
|
||||
<!-- prettier-ignore -->
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
transformManifest(manifest) { // [!code --]
|
||||
hooks: { // [!code ++]
|
||||
'build:manifestGenerated': (_, manifest) => { // [!code ++]
|
||||
// ...
|
||||
}, // [!code ++]
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### New Deprecations
|
||||
|
||||
#### `runner` APIs Renamed
|
||||
|
||||
To improve consistency with the `web-ext.config.ts` filename, the "runner" API and config options have been renamed. You can continue using the old names, but they have been deprecated and will be removed in a future version:
|
||||
|
||||
1. The `runner` option has been renamed to `webExt`:
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
runner: { // [!code --]
|
||||
webExt: { // [!code ++]
|
||||
startUrls: ["https://wxt.dev"],
|
||||
},
|
||||
});
|
||||
```
|
||||
2. `defineRunnerConfig` has been renamed to `defineWebExtConfig`:
|
||||
```ts [web-ext.config.ts]
|
||||
import { defineRunnerConfig } from 'wxt'; // [!code --]
|
||||
import { defineWebExtConfig } from 'wxt'; // [!code ++]
|
||||
```
|
||||
3. The `ExtensionRunnerConfig` type has been renamed to `WebExtConfig`
|
||||
```ts
|
||||
import type { ExtensionRunnerConfig } from 'wxt'; // [!code --]
|
||||
import type { WebExtConfig } from 'wxt'; // [!code ++]
|
||||
```
|
||||
|
||||
## v0.18.5 → v0.19.0
|
||||
|
||||
### `vite-node` Entrypoint Loader
|
||||
@@ -19,7 +277,7 @@ Currently, WXT is in pre-release. This means changes to the second digit, `v0.X`
|
||||
The default entrypoint loader has changed to `vite-node`. If you use any NPM packages that depend on the `webextension-polyfill`, you need to add them to Vite's `ssr.noExternal` option:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
vite: () => ({ // [!code ++]
|
||||
ssr: { // [!code ++]
|
||||
@@ -67,7 +325,7 @@ Basically, you can now import and do things outside the `main` function of the e
|
||||
|
||||
To continue using the old approach, add the following to your `wxt.config.ts` file:
|
||||
|
||||
```ts
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
entrypointLoader: 'jiti', // [!code ++]
|
||||
});
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
/guide/build-targets.html /guide/multiple-browsers.html
|
||||
/guide/installation.html /get-started/installation.html
|
||||
/guide/introduction.html /get-started/introduction.html
|
||||
/guide/upgrade-guide/wxt /guide/resources/upgrading.html
|
||||
/guide/upgrade-guide/wxt.html /guide/resources/upgrading.html
|
||||
|
||||
# 0.19.0
|
||||
/guide/go-further/entrypoint-side-effects.html /guide/go-further/entrypoint-loaders.html
|
||||
|
||||
+6
-6
@@ -15,7 +15,7 @@ A simplified wrapper around the extension storage APIs.
|
||||
This module is built-in to WXT, so you don't need to install anything.
|
||||
|
||||
```ts
|
||||
import { storage } from 'wxt/storage';
|
||||
import { storage } from '#imports';
|
||||
```
|
||||
|
||||
If you use auto-imports, `storage` is auto-imported for you, so you don't even need to import it!
|
||||
@@ -37,7 +37,7 @@ import { storage } from '@wxt-dev/storage';
|
||||
|
||||
## Storage Permission
|
||||
|
||||
To use the `wxt/storage` API, the `"storage"` permission must be added to the manifest:
|
||||
To use the `@wxt-dev/storage` API, the `"storage"` permission must be added to the manifest:
|
||||
|
||||
```ts [wxt.config.ts]
|
||||
export default defineConfig({
|
||||
@@ -74,7 +74,7 @@ await storage.watch<number>(
|
||||
await storage.getMeta<{ v: number }>('local:installDate');
|
||||
```
|
||||
|
||||
For a full list of methods available, see the [API reference](/api/reference/@wxt-dev/storage/interfaces/WxtStorage).
|
||||
For a full list of methods available, see the [API reference](/api/reference/wxt/utils/storage/interfaces/WxtStorage).
|
||||
|
||||
## Watchers
|
||||
|
||||
@@ -97,7 +97,7 @@ unwatch();
|
||||
|
||||
## Metadata
|
||||
|
||||
`wxt/storage` also supports setting metadata for keys, stored at `key + "$"`. Metadata is a collection of properties associated with a key. It might be a version number, last modified date, etc.
|
||||
`@wxt-dev/storage` also supports setting metadata for keys, stored at `key + "$"`. Metadata is a collection of properties associated with a key. It might be a version number, last modified date, etc.
|
||||
|
||||
[Other than versioning](#versioning), you are responsible for managing a field's metadata:
|
||||
|
||||
@@ -157,7 +157,7 @@ const unwatch = showChangelogOnUpdate.watch((newValue) => {
|
||||
});
|
||||
```
|
||||
|
||||
For a full list of properties and methods available, see the [API reference](/api/reference/@wxt-dev/storage/interfaces/WxtStorageItem).
|
||||
For a full list of properties and methods available, see the [API reference](/api/reference/wxt/utils/storage/interfaces/WxtStorageItem).
|
||||
|
||||
### Versioning
|
||||
|
||||
@@ -353,4 +353,4 @@ await storage.setItems([
|
||||
]);
|
||||
```
|
||||
|
||||
Refer to the [API Reference](/api/reference/@wxt-dev/storage/interfaces/WxtStorage) for types and examples of how to use all the bulk APIs.
|
||||
Refer to the [API Reference](/api/reference/wxt/utils/storage/interfaces/WxtStorage) for types and examples of how to use all the bulk APIs.
|
||||
|
||||
+2
-1
@@ -4,7 +4,7 @@
|
||||
"engines": {
|
||||
"node": ">=18.20.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.5.2",
|
||||
"packageManager": "pnpm@10.8.0",
|
||||
"scripts": {
|
||||
"check": "check && pnpm -r --sequential run check",
|
||||
"test": "pnpm -r --sequential run test run",
|
||||
@@ -26,6 +26,7 @@
|
||||
"changelogen": "catalog:",
|
||||
"consola": "catalog:",
|
||||
"fast-glob": "catalog:",
|
||||
"feed": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"markdown-it-footnote": "catalog:",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## v0.5.0
|
||||
[⚠️ breaking changes](https://wxt.dev/guide/resources/upgrading.html) • [compare changes](https://github.com/wxt-dev/wxt/compare/analytics-v0.4.1...analytics-v0.5.0)
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- ⚠️ Update min WXT version to 0.20 ([2e8baf0](https://github.com/wxt-dev/wxt/commit/2e8baf0))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineAppConfig } from 'wxt/sandbox';
|
||||
import { defineAppConfig } from 'wxt/utils/define-app-config';
|
||||
import { googleAnalytics4 } from './modules/analytics/providers/google-analytics-4';
|
||||
import { umami } from './modules/analytics/providers/umami';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'wxt';
|
||||
import 'wxt/sandbox';
|
||||
import 'wxt/utils/define-app-config';
|
||||
import {
|
||||
addAlias,
|
||||
addViteConfig,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { relative, resolve } from 'node:path';
|
||||
import type { AnalyticsConfig } from './types';
|
||||
|
||||
declare module 'wxt/sandbox' {
|
||||
declare module 'wxt/utils/define-app-config' {
|
||||
export interface WxtAppConfig {
|
||||
analytics: AnalyticsConfig;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export default defineWxtModule({
|
||||
? clientModuleId
|
||||
: relative(wxtAnalyticsFolder, clientModuleId)
|
||||
}';`,
|
||||
`import { useAppConfig } from 'wxt/client';`,
|
||||
`import { useAppConfig } from '#imports';`,
|
||||
``,
|
||||
`export const analytics = createAnalytics(useAppConfig().analytics);`,
|
||||
``,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wxt-dev/analytics",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"description": "Add analytics to your web extension",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -48,7 +48,7 @@
|
||||
"prepare": "buildc --deps-only -- wxt prepare"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"wxt": ">=0.19.23"
|
||||
"wxt": ">=0.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@wxt-dev/browser",
|
||||
"description": "Provides a cross-browser API for using extension APIs and types based on @types/chrome",
|
||||
"version": "0.0.310",
|
||||
"version": "0.0.315",
|
||||
"type": "module",
|
||||
"main": "src/index.mjs",
|
||||
"types": "src/index.d.ts",
|
||||
@@ -11,6 +11,7 @@
|
||||
"directory": "packages/browser"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "check",
|
||||
"gen": "tsx scripts/generate.ts"
|
||||
},
|
||||
"author": {
|
||||
@@ -22,10 +23,11 @@
|
||||
"src"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/chrome": "0.0.310",
|
||||
"@types/chrome": "0.0.315",
|
||||
"fs-extra": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { sep as posixSep } from 'node:path/posix';
|
||||
// Fetch latest version
|
||||
|
||||
console.log('Getting latest version of \x1b[36m@types/chrome\x1b[0m');
|
||||
await spawn('pnpm', ['i', '--ignore-scripts', '-D', '@types/chrome']);
|
||||
await spawn('pnpm', ['i', '--ignore-scripts', '-D', '@types/chrome@latest']);
|
||||
|
||||
// Generate new package.json
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// <reference types="chrome" />
|
||||
import { describe, expectTypeOf, it } from 'vitest';
|
||||
import { browser, Browser } from '../index';
|
||||
import { browser, type Browser } from '../index';
|
||||
|
||||
describe('browser', () => {
|
||||
describe('types', () => {
|
||||
|
||||
+291
-401
@@ -1867,198 +1867,146 @@ export namespace Browser {
|
||||
* Permissions: "contextMenus"
|
||||
*/
|
||||
export namespace contextMenus {
|
||||
export interface OnClickData {
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* The text for the context selection, if any.
|
||||
*/
|
||||
selectionText?: string | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* A flag indicating the state of a checkbox or radio item after it is clicked.
|
||||
*/
|
||||
checked?: boolean | undefined;
|
||||
/**
|
||||
* @since Chrome 35
|
||||
* The ID of the menu item that was clicked.
|
||||
*/
|
||||
menuItemId: number | string;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* The ID of the frame of the element where the context menu was
|
||||
* clicked, if it was in a frame.
|
||||
*/
|
||||
frameId?: number | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* The URL of the frame of the element where the context menu was clicked, if it was in a frame.
|
||||
*/
|
||||
frameUrl?: string | undefined;
|
||||
/**
|
||||
* @since Chrome 35
|
||||
* A flag indicating whether the element is editable (text input, textarea, etc.).
|
||||
*/
|
||||
editable: boolean;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements.
|
||||
*/
|
||||
mediaType?: "image" | "video" | "audio" | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* A flag indicating the state of a checkbox or radio item before it was clicked.
|
||||
*/
|
||||
wasChecked?: boolean | undefined;
|
||||
/**
|
||||
* @since Chrome 35
|
||||
* The URL of the page where the menu item was clicked. This property is not set if the click occurred in a context where there is no current page, such as in a launcher context menu.
|
||||
*/
|
||||
pageUrl: string;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* If the element is a link, the URL it points to.
|
||||
*/
|
||||
linkUrl?: string | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* The parent ID, if any, for the item clicked.
|
||||
*/
|
||||
parentMenuItemId?: number | string;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 35
|
||||
* Will be present for elements with a 'src' URL.
|
||||
*/
|
||||
srcUrl?: string | undefined;
|
||||
}
|
||||
|
||||
type ContextType =
|
||||
| "all"
|
||||
| "page"
|
||||
| "frame"
|
||||
| "selection"
|
||||
| "link"
|
||||
| "editable"
|
||||
| "image"
|
||||
| "video"
|
||||
| "audio"
|
||||
| "launcher"
|
||||
| "browser_action"
|
||||
| "page_action"
|
||||
| "action";
|
||||
|
||||
type ContextItemType = "normal" | "checkbox" | "radio" | "separator";
|
||||
|
||||
export interface CreateProperties {
|
||||
/** Optional. Lets you restrict the item to apply only to documents whose URL matches one of the given patterns. (This applies to frames as well.) For details on the format of a pattern, see Match Patterns. */
|
||||
documentUrlPatterns?: string[] | undefined;
|
||||
/** Optional. The initial state of a checkbox or radio item: true for selected and false for unselected. Only one radio item can be selected at a time in a given group of radio items. */
|
||||
checked?: boolean | undefined;
|
||||
/** Optional. The text to be displayed in the item; this is required unless type is 'separator'. When the context is 'selection', you can use %s within the string to show the selected text. For example, if this parameter's value is "Translate '%s' to Pig Latin" and the user selects the word "cool", the context menu item for the selection is "Translate 'cool' to Pig Latin". */
|
||||
title?: string | undefined;
|
||||
/** Optional. List of contexts this menu item will appear in. Defaults to ['page'] if not specified. */
|
||||
contexts?: ContextType | ContextType[] | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 20
|
||||
* Whether this context menu item is enabled or disabled. Defaults to true.
|
||||
*/
|
||||
enabled?: boolean | undefined;
|
||||
/** Optional. Similar to documentUrlPatterns, but lets you filter based on the src attribute of img/audio/video tags and the href of anchor tags. */
|
||||
targetUrlPatterns?: string[] | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* A function that will be called back when the menu item is clicked. Event pages cannot use this; instead, they should register a listener for Browser.contextMenus.onClicked.
|
||||
* @param info Information sent when a context menu item is clicked.
|
||||
* @param tab The details of the tab where the click took place. Note: this parameter only present for extensions.
|
||||
*/
|
||||
onclick?: ((info: OnClickData, tab: Browser.tabs.Tab) => void) | undefined;
|
||||
/** Optional. The ID of a parent menu item; this makes the item a child of a previously added item. */
|
||||
parentId?: number | string | undefined;
|
||||
/** Optional. The type of menu item. Defaults to 'normal' if not specified. */
|
||||
type?: ContextItemType | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 21
|
||||
* The unique ID to assign to this item. Mandatory for event pages. Cannot be the same as another ID for this extension.
|
||||
*/
|
||||
id?: string | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* @since Chrome 62
|
||||
* Whether the item is visible in the menu.
|
||||
*/
|
||||
visible?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface UpdateProperties extends Omit<CreateProperties, "id"> {}
|
||||
|
||||
export interface MenuClickedEvent
|
||||
extends Browser.events.Event<(info: OnClickData, tab?: Browser.tabs.Tab) => void>
|
||||
{}
|
||||
|
||||
/**
|
||||
* @since Chrome 38
|
||||
* The maximum number of top level extension items that can be added to an extension action context menu. Any items beyond this limit will be ignored.
|
||||
* The different contexts a menu can appear in. Specifying 'all' is equivalent to the combination of all other contexts except for 'launcher'. The 'launcher' context is only supported by apps and is used to add menu items to the context menu that appears when clicking the app icon in the launcher/taskbar/dock/etc. Different platforms might put limitations on what is actually supported in a launcher context menu.
|
||||
* @since Chrome 44
|
||||
*/
|
||||
export var ACTION_MENU_TOP_LEVEL_LIMIT: number;
|
||||
export enum ContextType {
|
||||
ALL = "all",
|
||||
PAGE = "page",
|
||||
FRAME = "frame",
|
||||
SELECTION = "selection",
|
||||
LINK = "link",
|
||||
EDITABLE = "editable",
|
||||
IMAGE = "image",
|
||||
VIDEO = "video",
|
||||
AUDIO = "audio",
|
||||
LAUNCHER = "launcher",
|
||||
BROWSER_ACTION = "browser_action",
|
||||
PAGE_ACTION = "page_action",
|
||||
ACTION = "action",
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all context menu items added by this extension.
|
||||
* Properties of the new context menu item.
|
||||
* @since Chrome 123
|
||||
*/
|
||||
export function removeAll(): Promise<void>;
|
||||
export interface CreateProperties {
|
||||
/** The initial state of a checkbox or radio button: `true` for selected, `false` for unselected. Only one radio button can be selected at a time in a given group. */
|
||||
checked?: boolean;
|
||||
/** List of contexts this menu item will appear in. Defaults to `['page']`. */
|
||||
contexts?: [`${ContextType}`, ...`${ContextType}`[]];
|
||||
/** Restricts the item to apply only to documents or frames whose URL matches one of the given patterns. For details on pattern formats, see Match Patterns. */
|
||||
documentUrlPatterns?: string[];
|
||||
/** Whether this context menu item is enabled or disabled. Defaults to `true`. */
|
||||
enabled?: boolean;
|
||||
/** The unique ID to assign to this item. Mandatory for event pages. Cannot be the same as another ID for this extension. */
|
||||
id?: string;
|
||||
/** The ID of a parent menu item; this makes the item a child of a previously added item. */
|
||||
parentId?: number | string;
|
||||
/** Similar to `documentUrlPatterns`, filters based on the `src` attribute of `img`, `audio`, and `video` tags and the `href` attribute of `a` tags. */
|
||||
targetUrlPatterns?: string[];
|
||||
/** The text to display in the item; this is _required_ unless `type` is `separator`. When the context is `selection`, use `%s` within the string to show the selected text. For example, if this parameter's value is "Translate '%s' to Pig Latin" and the user selects the word "cool", the context menu item for the selection is "Translate 'cool' to Pig Latin". */
|
||||
title?: string;
|
||||
/** The type of menu item. Defaults to `normal`. */
|
||||
type?: `${ItemType}`;
|
||||
/** Whether the item is visible in the menu. */
|
||||
visible?: boolean;
|
||||
/**
|
||||
* A function that is called back when the menu item is clicked. This is not available inside of a service worker; instead, you should register a listener for {@link contextMenus.onClicked}.
|
||||
* @param info Information about the item clicked and the context where the click happened.
|
||||
* @param tab The details of the tab where the click took place. This parameter is not present for platform apps.
|
||||
*/
|
||||
onclick?: (
|
||||
info: OnClickData,
|
||||
tab: tabs.Tab,
|
||||
) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all context menu items added by this extension.
|
||||
* @param callback Called when removal is complete.
|
||||
* The type of menu item.
|
||||
* @since Chrome 44
|
||||
*/
|
||||
export function removeAll(callback: () => void): void;
|
||||
export enum ItemType {
|
||||
NORMAL = "normal",
|
||||
CHECKBOX = "checkbox",
|
||||
RADIO = "radio",
|
||||
SEPARATOR = "separator",
|
||||
}
|
||||
|
||||
/** Information sent when a context menu item is clicked. */
|
||||
export interface OnClickData {
|
||||
/** A flag indicating the state of a checkbox or radio item after it is clicked. */
|
||||
checked?: boolean;
|
||||
/** A flag indicating whether the element is editable (text input, textarea, etc.). */
|
||||
editable: boolean;
|
||||
/**
|
||||
* The ID of the frame of the element where the context menu was clicked, if it was in a frame.
|
||||
* @since Chrome 51
|
||||
*/
|
||||
frameId?: number;
|
||||
/** The URL of the frame of the element where the context menu was clicked, if it was in a frame. */
|
||||
frameUrl?: string;
|
||||
/** If the element is a link, the URL it points to. */
|
||||
linkUrl?: string;
|
||||
/** One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements. */
|
||||
mediaType?: `${ContextType.IMAGE}` | `${ContextType.VIDEO}` | `${ContextType.AUDIO}`;
|
||||
/** The ID of the menu item that was clicked. */
|
||||
menuItemId: number | string;
|
||||
/** The URL of the page where the menu item was clicked. This property is not set if the click occurred in a context where there is no current page, such as in a launcher context menu. */
|
||||
pageUrl?: string;
|
||||
/** The parent ID, if any, for the item clicked.*/
|
||||
parentMenuItemId?: number | string;
|
||||
/** The text for the context selection, if any. */
|
||||
selectionText?: string | undefined;
|
||||
/** Will be present for elements with a 'src' URL. */
|
||||
srcUrl?: string | undefined;
|
||||
/** A flag indicating the state of a checkbox or radio item before it was clicked. */
|
||||
wasChecked?: boolean | undefined;
|
||||
}
|
||||
|
||||
/** The maximum number of top level extension items that can be added to an extension action context menu. Any items beyond this limit will be ignored. */
|
||||
export const ACTION_MENU_TOP_LEVEL_LIMIT: 6;
|
||||
|
||||
/**
|
||||
* Creates a new context menu item. Note that if an error occurs during creation, you may not find out until the creation callback fires (the details will be in Browser.runtime.lastError).
|
||||
* @param callback Called when the item has been created in the browser. If there were any problems creating the item, details will be available in Browser.runtime.lastError.
|
||||
* Creates a new context menu item. If an error occurs during creation, it may not be detected until the creation callback fires; details will be in {@link Browser.runtime.lastError}.
|
||||
* @return The ID of the newly created item.
|
||||
*/
|
||||
export function create(createProperties: CreateProperties, callback?: () => void): number | string;
|
||||
/**
|
||||
* Updates a previously created context menu item.
|
||||
* @param id The ID of the item to update.
|
||||
* @param updateProperties The properties to update. Accepts the same values as the create function.
|
||||
* @since Chrome 123
|
||||
*/
|
||||
export function update(id: string | number, updateProperties: UpdateProperties): Promise<void>;
|
||||
/**
|
||||
* Updates a previously created context menu item.
|
||||
* @param id The ID of the item to update.
|
||||
* @param updateProperties The properties to update. Accepts the same values as the create function.
|
||||
* @param callback Called when the context menu has been updated.
|
||||
*/
|
||||
export function update(id: string | number, updateProperties: UpdateProperties, callback: () => void): void;
|
||||
|
||||
/**
|
||||
* Removes a context menu item.
|
||||
* @param menuItemId The ID of the context menu item to remove.
|
||||
* @since Chrome 123
|
||||
*
|
||||
* Can return its result via Promise since Chrome 123.
|
||||
*/
|
||||
export function remove(menuItemId: string | number): Promise<void>;
|
||||
/**
|
||||
* Removes a context menu item.
|
||||
* @param menuItemId The ID of the context menu item to remove.
|
||||
* @param callback Called when the context menu has been removed.
|
||||
*/
|
||||
export function remove(menuItemId: string | number, callback: () => void): void;
|
||||
|
||||
/**
|
||||
* @since Chrome 21
|
||||
* Fired when a context menu item is clicked.
|
||||
* Removes all context menu items added by this extension.
|
||||
*
|
||||
* Can return its result via Promise since Chrome 123.
|
||||
*/
|
||||
export var onClicked: MenuClickedEvent;
|
||||
export function removeAll(): Promise<void>;
|
||||
export function removeAll(callback: () => void): void;
|
||||
|
||||
/**
|
||||
* Updates a previously created context menu item.
|
||||
* @param id The ID of the item to update.
|
||||
* @param updateProperties The properties to update. Accepts the same values as the {@link contextMenus.create} function.
|
||||
*
|
||||
* Can return its result via Promise since Chrome 123.
|
||||
*/
|
||||
export function update(id: string | number, updateProperties: Omit<CreateProperties, "id">): Promise<void>;
|
||||
export function update(
|
||||
id: string | number,
|
||||
updateProperties: Omit<CreateProperties, "id">,
|
||||
callback: () => void,
|
||||
): void;
|
||||
|
||||
/** Fired when a context menu item is clicked. */
|
||||
export const onClicked: events.Event<(info: OnClickData, tab?: tabs.Tab) => void>;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
@@ -3073,6 +3021,71 @@ export namespace Browser {
|
||||
export var themeName: "default" | "dark";
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Dev Tools - Recorder
|
||||
////////////////////
|
||||
/**
|
||||
* Use the `Browser.devtools.recorder` API to customize the Recorder panel in DevTools.
|
||||
* @since Chrome 105
|
||||
*/
|
||||
export namespace devtools.recorder {
|
||||
/** A plugin interface that the Recorder panel invokes to customize the Recorder panel. */
|
||||
export interface RecorderExtensionPlugin {
|
||||
/**
|
||||
* Allows the extension to implement custom replay functionality.
|
||||
*
|
||||
* @param recording A recording of the user interaction with the page. This should match [Puppeteer's recording schema](https://github.com/puppeteer/replay/blob/main/docs/api/interfaces/Schema.UserFlow.md).
|
||||
* @since Chrome 112
|
||||
*/
|
||||
replay?(recording: object): void;
|
||||
|
||||
/**
|
||||
* Converts a recording from the Recorder panel format into a string.
|
||||
* @param recording A recording of the user interaction with the page. This should match [Puppeteer's recording schema](https://github.com/puppeteer/replay/blob/main/docs/api/interfaces/Schema.UserFlow.md).
|
||||
*/
|
||||
stringify?(recording: object): void;
|
||||
|
||||
/**
|
||||
* Converts a step of the recording from the Recorder panel format into a string.
|
||||
* @param step A step of the recording of a user interaction with the page. This should match [Puppeteer's step schema](https://github.com/puppeteer/replay/blob/main/docs/api/modules/Schema.md#step).
|
||||
*/
|
||||
stringifyStep?(step: object): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a view created by extension to be embedded inside the Recorder panel.
|
||||
* @since Chrome 112
|
||||
*/
|
||||
export interface RecorderView {
|
||||
/** Fired when the view is hidden. */
|
||||
onHidden: events.Event<() => void>;
|
||||
/** Fired when the view is shown. */
|
||||
onShown: events.Event<() => void>;
|
||||
/** Indicates that the extension wants to show this view in the Recorder panel. */
|
||||
show(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a view that can handle the replay. This view will be embedded inside the Recorder panel.
|
||||
* @param title Title that is displayed next to the extension icon in the Developer Tools toolbar.
|
||||
* @param pagePath Path of the panel's HTML page relative to the extension directory.
|
||||
* @since Chrome 112
|
||||
*/
|
||||
export function createView(title: string, pagePath: string): RecorderView;
|
||||
|
||||
/**
|
||||
* Registers a Recorder extension plugin.
|
||||
* @param plugin An instance implementing the RecorderExtensionPlugin interface.
|
||||
* @param name The name of the plugin.
|
||||
* @param mediaType The media type of the string content that the plugin produces.
|
||||
*/
|
||||
export function registerRecorderExtensionPlugin(
|
||||
plugin: RecorderExtensionPlugin,
|
||||
name: string,
|
||||
mediaType: string,
|
||||
): void;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Document Scan
|
||||
////////////////////
|
||||
@@ -9798,32 +9811,6 @@ export namespace Browser {
|
||||
export function updateContentScripts(scripts: RegisteredContentScript[], callback: () => void): void;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Script Badge
|
||||
////////////////////
|
||||
export namespace scriptBadge {
|
||||
export interface GetPopupDetails {
|
||||
tabId: number;
|
||||
}
|
||||
|
||||
export interface AttentionDetails {
|
||||
tabId: number;
|
||||
}
|
||||
|
||||
export interface SetPopupDetails {
|
||||
tabId: number;
|
||||
popup: string;
|
||||
}
|
||||
|
||||
export interface ScriptBadgeClickedEvent extends Browser.events.Event<(tab: Browser.tabs.Tab) => void> {}
|
||||
|
||||
export function getPopup(details: GetPopupDetails, callback: Function): void;
|
||||
export function getAttention(details: AttentionDetails): void;
|
||||
export function setPopup(details: SetPopupDetails): void;
|
||||
|
||||
export var onClicked: ScriptBadgeClickedEvent;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Sessions
|
||||
////////////////////
|
||||
@@ -12382,133 +12369,143 @@ export namespace Browser {
|
||||
// Text to Speech
|
||||
////////////////////
|
||||
/**
|
||||
* Use the `Browser.tts` API to play synthesized text-to-speech (TTS). See also the related ttsEngine API, which allows an extension to implement a speech engine.
|
||||
* Use the `Browser.tts` API to play synthesized text-to-speech (TTS). See also the related {@link ttsEngine} API, which allows an extension to implement a speech engine.
|
||||
*
|
||||
* Permissions: "tts"
|
||||
*/
|
||||
export namespace tts {
|
||||
/** @since Chrome 54 */
|
||||
export enum EventType {
|
||||
START = "start",
|
||||
END = "end",
|
||||
WORD = "word",
|
||||
SENTENCE = "sentence",
|
||||
MARKER = "marker",
|
||||
INTERRUPTED = "interrupted",
|
||||
CANCELLED = "cancelled",
|
||||
ERROR = "error",
|
||||
PAUSE = "pause",
|
||||
RESUME = "resume",
|
||||
}
|
||||
|
||||
/** An event from the TTS engine to communicate the status of an utterance. */
|
||||
export interface TtsEvent {
|
||||
/** Optional. The index of the current character in the utterance. */
|
||||
charIndex?: number | undefined;
|
||||
/** Optional. The error description, if the event type is 'error'. */
|
||||
errorMessage?: string | undefined;
|
||||
/** The index of the current character in the utterance. For word events, the event fires at the end of one word and before the beginning of the next. The `charIndex` represents a point in the text at the beginning of the next word to be spoken. */
|
||||
charIndex?: number;
|
||||
/** The error description, if the event type is `error`. */
|
||||
errorMessage?: string;
|
||||
/**
|
||||
* The length of the next part of the utterance.
|
||||
* For example, in a word event, this is the length of the word which will be spoken next.
|
||||
* It will be set to -1 if not set by the speech engine.
|
||||
* The length of the next part of the utterance. For example, in a `word` event, this is the length of the word which will be spoken next. It will be set to -1 if not set by the speech engine.
|
||||
* @since Chrome 74
|
||||
*/
|
||||
length?: number | undefined;
|
||||
length?: number;
|
||||
/** The type can be `start` as soon as speech has started, `word` when a word boundary is reached, `sentence` when a sentence boundary is reached, `marker` when an SSML mark element is reached, `end` when the end of the utterance is reached, `interrupted` when the utterance is stopped or interrupted before reaching the end, `cancelled` when it's removed from the queue before ever being synthesized, or `error` when any other error occurs. When pausing speech, a `pause` event is fired if a particular utterance is paused in the middle, and `resume` if an utterance resumes speech. Note that pause and resume events may not fire if speech is paused in-between utterances. */
|
||||
type: `${EventType}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The speech options for the TTS engine.
|
||||
* @since Chrome 77
|
||||
*/
|
||||
export interface TtsOptions {
|
||||
/** The TTS event types that you are interested in listening to. If missing, all event types may be sent. */
|
||||
desiredEventTypes?: string[];
|
||||
/** If true, enqueues this utterance if TTS is already in progress. If false (the default), interrupts any current speech and flushes the speech queue before speaking this new utterance. */
|
||||
enqueue?: boolean;
|
||||
/** The extension ID of the speech engine to use, if known. */
|
||||
extensionId?: string;
|
||||
/**
|
||||
* The type can be 'start' as soon as speech has started, 'word' when a word boundary is reached, 'sentence' when a sentence boundary is reached, 'marker' when an SSML mark element is reached, 'end' when the end of the utterance is reached, 'interrupted' when the utterance is stopped or interrupted before reaching the end, 'cancelled' when it's removed from the queue before ever being synthesized, or 'error' when any other error occurs. When pausing speech, a 'pause' event is fired if a particular utterance is paused in the middle, and 'resume' if an utterance resumes speech. Note that pause and resume events may not fire if speech is paused in-between utterances.
|
||||
* One of: "start", "end", "word", "sentence", "marker", "interrupted", "cancelled", "error", "pause", or "resume"
|
||||
* Gender of voice for synthesized speech.
|
||||
* @deprecated since Chrome 77. Gender is deprecated and will be ignored.
|
||||
*/
|
||||
type:
|
||||
| "start"
|
||||
| "end"
|
||||
| "word"
|
||||
| "sentence"
|
||||
| "marker"
|
||||
| "interrupted"
|
||||
| "cancelled"
|
||||
| "error"
|
||||
| "pause"
|
||||
| "resume";
|
||||
gender?: `${VoiceGender}`;
|
||||
/** The language to be used for synthesis, in the form _language_\-_region_. Examples: 'en', 'en-US', 'en-GB', 'zh-CN'. */
|
||||
lang?: string;
|
||||
/** Speaking pitch between 0 and 2 inclusive, with 0 being lowest and 2 being highest. 1.0 corresponds to a voice's default pitch. */
|
||||
pitch?: number;
|
||||
/** Speaking rate relative to the default rate for this voice. 1.0 is the default rate, normally around 180 to 220 words per minute. 2.0 is twice as fast, and 0.5 is half as fast. Values below 0.1 or above 10.0 are strictly disallowed, but many voices will constrain the minimum and maximum rates further—for example a particular voice may not actually speak faster than 3 times normal even if you specify a value larger than 3.0. */
|
||||
rate?: number;
|
||||
/** The TTS event types the voice must support. */
|
||||
requiredEventTypes?: string[];
|
||||
/** The name of the voice to use for synthesis. If empty, uses any available voice. */
|
||||
voiceName?: string;
|
||||
/** Speaking volume between 0 and 1 inclusive, with 0 being lowest and 1 being highest, with a default of 1.0. */
|
||||
volume?: number;
|
||||
/**
|
||||
* This function is called with events that occur in the process of speaking the utterance.
|
||||
* @param event The update event from the text-to-speech engine indicating the status of this utterance.
|
||||
*/
|
||||
onEvent?: (
|
||||
event: TtsEvent,
|
||||
) => void;
|
||||
}
|
||||
|
||||
/** A description of a voice available for speech synthesis. */
|
||||
export interface TtsVoice {
|
||||
/** Optional. The language that this voice supports, in the form language-region. Examples: 'en', 'en-US', 'en-GB', 'zh-CN'. */
|
||||
lang?: string | undefined;
|
||||
/** All of the callback event types that this voice is capable of sending. */
|
||||
eventTypes?: `${EventType}`[];
|
||||
/** The ID of the extension providing this voice. */
|
||||
extensionId?: string;
|
||||
/**
|
||||
* Optional. This voice's gender.
|
||||
* One of: "male", or "female"
|
||||
* This voice's gender.
|
||||
* @deprecated since Chrome 70. Gender is deprecated and will be ignored.
|
||||
*/
|
||||
gender?: string | undefined;
|
||||
/** Optional. The name of the voice. */
|
||||
voiceName?: string | undefined;
|
||||
/** Optional. The ID of the extension providing this voice. */
|
||||
extensionId?: string | undefined;
|
||||
/** Optional. All of the callback event types that this voice is capable of sending. */
|
||||
eventTypes?: string[] | undefined;
|
||||
/**
|
||||
* Optional. If true, the synthesis engine is a remote network resource. It may be higher latency and may incur bandwidth costs.
|
||||
* @since Chrome 33
|
||||
*/
|
||||
remote?: boolean | undefined;
|
||||
gender?: `${VoiceGender}`;
|
||||
/** The language that this voice supports, in the form language-region. Examples: 'en', 'en-US', 'en-GB', 'zh-CN'. */
|
||||
lang?: string;
|
||||
/** If true, the synthesis engine is a remote network resource. It may be higher latency and may incur bandwidth costs. */
|
||||
remote?: boolean;
|
||||
/** The name of the voice. */
|
||||
voiceName?: string;
|
||||
}
|
||||
|
||||
export interface SpeakOptions {
|
||||
/** Optional. Speaking volume between 0 and 1 inclusive, with 0 being lowest and 1 being highest, with a default of 1.0. */
|
||||
volume?: number | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* If true, enqueues this utterance if TTS is already in progress. If false (the default), interrupts any current speech and flushes the speech queue before speaking this new utterance.
|
||||
*/
|
||||
enqueue?: boolean | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* Speaking rate relative to the default rate for this voice. 1.0 is the default rate, normally around 180 to 220 words per minute. 2.0 is twice as fast, and 0.5 is half as fast. Values below 0.1 or above 10.0 are strictly disallowed, but many voices will constrain the minimum and maximum rates further—for example a particular voice may not actually speak faster than 3 times normal even if you specify a value larger than 3.0.
|
||||
*/
|
||||
rate?: number | undefined;
|
||||
/**
|
||||
* Optional. This function is called with events that occur in the process of speaking the utterance.
|
||||
* @param event The update event from the text-to-speech engine indicating the status of this utterance.
|
||||
*/
|
||||
onEvent?: ((event: TtsEvent) => void) | undefined;
|
||||
/**
|
||||
* Optional.
|
||||
* Speaking pitch between 0 and 2 inclusive, with 0 being lowest and 2 being highest. 1.0 corresponds to a voice's default pitch.
|
||||
*/
|
||||
pitch?: number | undefined;
|
||||
/** Optional. The language to be used for synthesis, in the form language-region. Examples: 'en', 'en-US', 'en-GB', 'zh-CN'. */
|
||||
lang?: string | undefined;
|
||||
/** Optional. The name of the voice to use for synthesis. If empty, uses any available voice. */
|
||||
voiceName?: string | undefined;
|
||||
/** Optional. The extension ID of the speech engine to use, if known. */
|
||||
extensionId?: string | undefined;
|
||||
/**
|
||||
* Optional. Gender of voice for synthesized speech.
|
||||
* One of: "male", or "female"
|
||||
*/
|
||||
gender?: string | undefined;
|
||||
/** Optional. The TTS event types the voice must support. */
|
||||
requiredEventTypes?: string[] | undefined;
|
||||
/** Optional. The TTS event types that you are interested in listening to. If missing, all event types may be sent. */
|
||||
desiredEventTypes?: string[] | undefined;
|
||||
/** @deprecated since Chrome 70. Gender is deprecated and is ignored.*/
|
||||
export enum VoiceGender {
|
||||
FEMALE = "female",
|
||||
MALE = "male",
|
||||
}
|
||||
|
||||
/** Checks whether the engine is currently speaking. On Mac OS X, the result is true whenever the system speech engine is speaking, even if the speech wasn't initiated by Chrome. */
|
||||
export function isSpeaking(callback?: (speaking: boolean) => void): void;
|
||||
/** Stops any current speech and flushes the queue of any pending utterances. In addition, if speech was paused, it will now be un-paused for the next call to speak. */
|
||||
export function stop(): void;
|
||||
/** Gets an array of all available voices. */
|
||||
export function getVoices(): Promise<TtsVoice[]>;
|
||||
export function getVoices(callback?: (voices: TtsVoice[]) => void): void;
|
||||
/**
|
||||
* Speaks text using a text-to-speech engine.
|
||||
* @param utterance The text to speak, either plain text or a complete, well-formed SSML document. Speech engines that do not support SSML will strip away the tags and speak the text. The maximum length of the text is 32,768 characters.
|
||||
* @param callback Optional. Called right away, before speech finishes. Check Browser.runtime.lastError to make sure there were no errors. Use options.onEvent to get more detailed feedback.
|
||||
* Gets an array of all available voices.
|
||||
*
|
||||
* Can return its result via Promise since Chrome Chrome 101
|
||||
*/
|
||||
export function speak(utterance: string, callback?: Function): void;
|
||||
export function getVoices(): Promise<TtsVoice[]>;
|
||||
export function getVoices(callback: (voices: TtsVoice[]) => void): void;
|
||||
|
||||
/**
|
||||
* Checks whether the engine is currently speaking. On Mac OS X, the result is true whenever the system speech engine is speaking, even if the speech wasn't initiated by Chrome.
|
||||
*
|
||||
* Can return its result via Promise since Chrome Chrome 101
|
||||
*/
|
||||
export function isSpeaking(): Promise<boolean>;
|
||||
export function isSpeaking(callback: (speaking: boolean) => void): void;
|
||||
|
||||
/** Pauses speech synthesis, potentially in the middle of an utterance. A call to resume or stop will un-pause speech. */
|
||||
export function pause(): void;
|
||||
|
||||
/** If speech was paused, resumes speaking where it left off. */
|
||||
export function resume(): void;
|
||||
|
||||
/**
|
||||
* Speaks text using a text-to-speech engine.
|
||||
* @param utterance The text to speak, either plain text or a complete, well-formed SSML document. Speech engines that do not support SSML will strip away the tags and speak the text. The maximum length of the text is 32,768 characters.
|
||||
* @param options Optional. The speech options.
|
||||
* @param callback Optional. Called right away, before speech finishes. Check Browser.runtime.lastError to make sure there were no errors. Use options.onEvent to get more detailed feedback.
|
||||
|
||||
* Can return its result via Promise since Chrome Chrome 101
|
||||
*/
|
||||
export function speak(utterance: string, options: SpeakOptions, callback?: Function): void;
|
||||
export function speak(utterance: string, options?: TtsOptions): Promise<void>;
|
||||
export function speak(utterance: string, callback: () => void): void;
|
||||
export function speak(utterance: string, options: TtsOptions, callback: () => void): void;
|
||||
|
||||
/** Stops any current speech and flushes the queue of any pending utterances. In addition, if speech was paused, it will now be un-paused for the next call to speak. */
|
||||
export function stop(): void;
|
||||
|
||||
/**
|
||||
* Pauses speech synthesis, potentially in the middle of an utterance. A call to resume or stop will un-pause speech.
|
||||
* @since Chrome 29
|
||||
* Called when the list of {@link TtsVoice} that would be returned by getVoices has changed.
|
||||
* @since Chrome 124
|
||||
*/
|
||||
export function pause(): void;
|
||||
/**
|
||||
* If speech was paused, resumes speaking where it left off.
|
||||
* @since Chrome 29
|
||||
*/
|
||||
export function resume(): void;
|
||||
const onVoicesChanged: Browser.events.Event<() => void>;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
@@ -13445,113 +13442,6 @@ export namespace Browser {
|
||||
export const onErrorOccurred: WebResponseErrorEvent;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Web Store
|
||||
////////////////////
|
||||
/**
|
||||
* Use the Browser.webstore API to initiate app and extension installations "inline" from your site.
|
||||
* @since Chrome 15
|
||||
*/
|
||||
export namespace webstore {
|
||||
/**
|
||||
* @param url Optional. If you have more than one <link> tag on your page with the chrome-webstore-item relation, you can choose which item you'd like to install by passing in its URL here. If it is omitted, then the first (or only) link will be used. An exception will be thrown if the passed in URL does not exist on the page.
|
||||
* @param successCallback Optional. This function is invoked when inline installation successfully completes (after the dialog is shown and the user agrees to add the item to Chrome). You may wish to use this to hide the user interface element that prompted the user to install the app or extension.
|
||||
* @param failureCallback Optional. This function is invoked when inline installation does not successfully complete. Possible reasons for this include the user canceling the dialog, the linked item not being found in the store, or the install being initiated from a non-verified site.
|
||||
* Parameter error: The failure detail. You may wish to inspect or log this for debugging purposes, but you should not rely on specific strings being passed back.
|
||||
* Optional parameter errorCode: The error code from the stable set of possible errors.
|
||||
* * Enum of the possible install results, including error codes sent back in the event that an inline installation has failed.
|
||||
* * * "otherError": An uncommon, unrecognized, or unexpected error. In some cases, the readable error string can provide more information.
|
||||
* * * "aborted": The operation was aborted as the requestor is no longer alive.
|
||||
* * * "installInProgress": An installation of the same extension is in progress.
|
||||
* * * "notPermitted": The installation is not permitted.
|
||||
* * * "invalidId": Invalid Chrome Web Store item ID.
|
||||
* * * "webstoreRequestError": Failed to retrieve extension metadata from the Web Store.
|
||||
* * * "invalidWebstoreResponse": The extension metadata retrieved from the Web Store was invalid.
|
||||
* * * "invalidManifest": An error occurred while parsing the extension manifest retrieved from the Web Store.
|
||||
* * * "iconError": Failed to retrieve the extension's icon from the Web Store, or the icon was invalid.
|
||||
* * * "userCanceled": The user canceled the operation.
|
||||
* * * "blacklisted": The extension is blacklisted.
|
||||
* * * "missingDependencies": Unsatisfied dependencies, such as shared modules.
|
||||
* * * "requirementViolations": Unsatisfied requirements, such as webgl.
|
||||
* * * "blockedByPolicy": The extension is blocked by management policies.
|
||||
* * * "launchFeatureDisabled": The launch feature is not available.
|
||||
* * * "launchUnsupportedExtensionType": The launch feature is not supported for the extension type.
|
||||
* * * "launchInProgress": A launch of the same extension is in progress.
|
||||
*/
|
||||
export function install(
|
||||
url: string,
|
||||
successCallback?: Function,
|
||||
failureCallback?: (error: string, errorCode?: string) => void,
|
||||
): void;
|
||||
/**
|
||||
* @param successCallback Optional. This function is invoked when inline installation successfully completes (after the dialog is shown and the user agrees to add the item to Chrome). You may wish to use this to hide the user interface element that prompted the user to install the app or extension.
|
||||
* @param failureCallback Optional. This function is invoked when inline installation does not successfully complete. Possible reasons for this include the user canceling the dialog, the linked item not being found in the store, or the install being initiated from a non-verified site.
|
||||
* Parameter error: The failure detail. You may wish to inspect or log this for debugging purposes, but you should not rely on specific strings being passed back.
|
||||
* Optional parameter errorCode: The error code from the stable set of possible errors.
|
||||
* * Enum of the possible install results, including error codes sent back in the event that an inline installation has failed.
|
||||
* * * "otherError": An uncommon, unrecognized, or unexpected error. In some cases, the readable error string can provide more information.
|
||||
* * * "aborted": The operation was aborted as the requestor is no longer alive.
|
||||
* * * "installInProgress": An installation of the same extension is in progress.
|
||||
* * * "notPermitted": The installation is not permitted.
|
||||
* * * "invalidId": Invalid Chrome Web Store item ID.
|
||||
* * * "webstoreRequestError": Failed to retrieve extension metadata from the Web Store.
|
||||
* * * "invalidWebstoreResponse": The extension metadata retrieved from the Web Store was invalid.
|
||||
* * * "invalidManifest": An error occurred while parsing the extension manifest retrieved from the Web Store.
|
||||
* * * "iconError": Failed to retrieve the extension's icon from the Web Store, or the icon was invalid.
|
||||
* * * "userCanceled": The user canceled the operation.
|
||||
* * * "blacklisted": The extension is blacklisted.
|
||||
* * * "missingDependencies": Unsatisfied dependencies, such as shared modules.
|
||||
* * * "requirementViolations": Unsatisfied requirements, such as webgl.
|
||||
* * * "blockedByPolicy": The extension is blocked by management policies.
|
||||
* * * "launchFeatureDisabled": The launch feature is not available.
|
||||
* * * "launchUnsupportedExtensionType": The launch feature is not supported for the extension type.
|
||||
* * * "launchInProgress": A launch of the same extension is in progress.
|
||||
*/
|
||||
export function install(
|
||||
successCallback: Function,
|
||||
failureCallback?: (error: string, errorCode?: string) => void,
|
||||
): void;
|
||||
/**
|
||||
* @param failureCallback Optional. This function is invoked when inline installation does not successfully complete. Possible reasons for this include the user canceling the dialog, the linked item not being found in the store, or the install being initiated from a non-verified site.
|
||||
* Parameter error: The failure detail. You may wish to inspect or log this for debugging purposes, but you should not rely on specific strings being passed back.
|
||||
* Optional parameter errorCode: The error code from the stable set of possible errors.
|
||||
* * Enum of the possible install results, including error codes sent back in the event that an inline installation has failed.
|
||||
* * * "otherError": An uncommon, unrecognized, or unexpected error. In some cases, the readable error string can provide more information.
|
||||
* * * "aborted": The operation was aborted as the requestor is no longer alive.
|
||||
* * * "installInProgress": An installation of the same extension is in progress.
|
||||
* * * "notPermitted": The installation is not permitted.
|
||||
* * * "invalidId": Invalid Chrome Web Store item ID.
|
||||
* * * "webstoreRequestError": Failed to retrieve extension metadata from the Web Store.
|
||||
* * * "invalidWebstoreResponse": The extension metadata retrieved from the Web Store was invalid.
|
||||
* * * "invalidManifest": An error occurred while parsing the extension manifest retrieved from the Web Store.
|
||||
* * * "iconError": Failed to retrieve the extension's icon from the Web Store, or the icon was invalid.
|
||||
* * * "userCanceled": The user canceled the operation.
|
||||
* * * "blacklisted": The extension is blacklisted.
|
||||
* * * "missingDependencies": Unsatisfied dependencies, such as shared modules.
|
||||
* * * "requirementViolations": Unsatisfied requirements, such as webgl.
|
||||
* * * "blockedByPolicy": The extension is blocked by management policies.
|
||||
* * * "launchFeatureDisabled": The launch feature is not available.
|
||||
* * * "launchUnsupportedExtensionType": The launch feature is not supported for the extension type.
|
||||
* * * "launchInProgress": A launch of the same extension is in progress.
|
||||
*/
|
||||
export function install(failureCallback?: (error: string, errorCode?: string) => void): void;
|
||||
|
||||
export interface InstallationStageEvent extends Browser.events.Event<(stage: string) => void> {}
|
||||
|
||||
export interface DownloadProgressEvent extends Browser.events.Event<(percentDownloaded: number) => void> {}
|
||||
|
||||
/**
|
||||
* Fired when an inline installation enters a new InstallStage. In order to receive notifications about this event, listeners must be registered before the inline installation begins.
|
||||
* @since Chrome 35
|
||||
*/
|
||||
export var onInstallStageChanged: InstallationStageEvent;
|
||||
/**
|
||||
* Fired periodically with the download progress of an inline install. In order to receive notifications about this event, listeners must be registered before the inline installation begins.
|
||||
* @since Chrome 35
|
||||
*/
|
||||
export var onDownloadProgress: DownloadProgressEvent;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Windows
|
||||
////////////////////
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"directory": "packages/browser"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "check",
|
||||
"gen": "tsx scripts/generate.ts"
|
||||
},
|
||||
"author": {
|
||||
@@ -26,6 +27,7 @@
|
||||
"fs-extra": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ However, it does have one major downside:
|
||||
helloWorld: Hello world!
|
||||
```
|
||||
|
||||
> `@wxt-dev/i18n` supports the standard messages format, so if you already have localization files at `<srcDir>/public/_locale/<lang>/messages.json`, you don't need to convert them to YAML or refactor them - just move them to `<srcDir>/locales/<lang>.json` and they'll just work out of the box!
|
||||
> `@wxt-dev/i18n` supports the standard messages format, so if you already have localization files at `<rootDir>/public/_locale/<lang>/messages.json`, you don't need to convert them to YAML or refactor them - just move them to `<srcDir>/locales/<lang>.json` and they'll just work out of the box!
|
||||
|
||||
4. To get a translation, use the auto-imported `i18n` object or import it manually:
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { defineContentScript } from 'wxt/sandbox';
|
||||
import { ContentScriptContext, createShadowRootUi } from 'wxt/client';
|
||||
import {
|
||||
defineContentScript,
|
||||
ContentScriptContext,
|
||||
createShadowRootUi,
|
||||
} from '#imports';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ export default defineWxtModule<ReactModuleOptions>({
|
||||
|
||||
// Enable auto-imports for JSX files
|
||||
wxt.hook('config:resolved', (wxt) => {
|
||||
if (wxt.config.imports === false) return;
|
||||
// In older versions of WXT, `wxt.config.imports` could be false
|
||||
if (!wxt.config.imports) return;
|
||||
|
||||
wxt.config.imports.dirsScanOptions ??= {};
|
||||
wxt.config.imports.dirsScanOptions.filePatterns = [
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { defineContentScript } from 'wxt/sandbox';
|
||||
import { ContentScriptContext, createShadowRootUi } from 'wxt/client';
|
||||
import {
|
||||
defineContentScript,
|
||||
ContentScriptContext,
|
||||
createShadowRootUi,
|
||||
} from '#imports';
|
||||
import { render } from 'solid-js/web';
|
||||
|
||||
export default defineContentScript({
|
||||
|
||||
@@ -19,7 +19,8 @@ export default defineWxtModule<SolidModuleOptions>({
|
||||
|
||||
// Enable auto-imports for JSX files
|
||||
wxt.hook('config:resolved', (wxt) => {
|
||||
if (wxt.config.imports === false) return;
|
||||
// In older versions of WXT, `wxt.config.imports` could be false
|
||||
if (!wxt.config.imports) return;
|
||||
|
||||
wxt.config.imports.dirsScanOptions ??= {};
|
||||
wxt.config.imports.dirsScanOptions.filePatterns = [
|
||||
|
||||
@@ -979,7 +979,7 @@ describe('Storage Utils', () => {
|
||||
|
||||
const actual = await item.getMeta();
|
||||
|
||||
expect(actual).toBe(expected);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return an empty object if missing', async () => {
|
||||
|
||||
@@ -24,7 +24,7 @@ export default defineConfig({
|
||||
Now in your entrypoint, import UnoCSS:
|
||||
|
||||
```ts
|
||||
import 'uno.css';
|
||||
import 'virtual:uno.css';
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# `@wxt-dev/webextension-polyfill`
|
||||
|
||||
Configures `wxt/browser` to import `browser` from [`webextension-polyfill`](https://github.com/mozilla/webextension-polyfill) instead of using the regular `chrome`/`browser` globals WXT normally provides.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
pnpm i @wxt-dev/webextension-polyfill webextension-polyfill
|
||||
```
|
||||
|
||||
Then add the module to your config:
|
||||
|
||||
```ts
|
||||
// wxt.config.ts
|
||||
export default defineConfig({
|
||||
modules: ['@wxt-dev/webextension-polyfill'],
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
export default defineBuildConfig({
|
||||
rootDir: resolve(__dirname, 'modules/webextension-polyfill'),
|
||||
outDir: resolve(__dirname, 'dist'),
|
||||
entries: [
|
||||
{ input: 'index.ts', name: 'index' },
|
||||
{ input: 'browser.ts', name: 'browser' },
|
||||
],
|
||||
replace: {
|
||||
'process.env.NPM': 'true',
|
||||
},
|
||||
declaration: true,
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export default defineContentScript({
|
||||
matches: ['*://*/*'],
|
||||
async main() {
|
||||
console.log(browser.runtime.id);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
const root = document.getElementById('app')!;
|
||||
|
||||
root.textContent = browser.runtime.id;
|
||||
@@ -0,0 +1 @@
|
||||
export { default as browser } from 'webextension-polyfill';
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'wxt';
|
||||
import { addViteConfig, defineWxtModule } from 'wxt/modules';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
export default defineWxtModule({
|
||||
name: '@wxt-dev/webextension-polyfill',
|
||||
setup(wxt) {
|
||||
addViteConfig(wxt, () => ({
|
||||
resolve: {
|
||||
alias: {
|
||||
'wxt/browser': process.env.NPM
|
||||
? '@wxt-dev/webextension-polyfill/browser'
|
||||
: resolve(__dirname, 'browser.ts'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "@wxt-dev/webextension-polyfill",
|
||||
"description": "Use webextension-polyfill with WXT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wxt-dev/wxt.git",
|
||||
"directory": "packages/webextension-polyfill"
|
||||
},
|
||||
"homepage": "https://github.com/wxt-dev/wxt/blob/main/packages/webextension-polyfill/README.md",
|
||||
"keywords": [
|
||||
"wxt",
|
||||
"module",
|
||||
"webextension-polyfill"
|
||||
],
|
||||
"author": {
|
||||
"name": "Aaron Klinker",
|
||||
"email": "aaronklinker1+wxt@gmail.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./dist/browser.d.mts",
|
||||
"default": "./dist/browser.mjs"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
"check": "pnpm build && check",
|
||||
"build": "buildc -- unbuild",
|
||||
"prepare": "buildc --deps-only -- wxt prepare"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webextension-polyfill": "*",
|
||||
"wxt": ">=0.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@types/webextension-polyfill": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"webextension-polyfill": "catalog:",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": ["../../tsconfig.base.json", "./.wxt/tsconfig.json"],
|
||||
"exclude": ["node_modules/**", "dist/**"]
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
"react-dom": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chrome": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@wxt-dev/auto-icons": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineAppConfig } from 'wxt/sandbox';
|
||||
import { defineAppConfig } from '#imports';
|
||||
|
||||
declare module 'wxt/sandbox' {
|
||||
declare module 'wxt/utils/define-app-config' {
|
||||
export interface WxtAppConfig {
|
||||
example: string;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import background from '../background';
|
||||
|
||||
chrome.i18n.getMessage = () => 'fake-message';
|
||||
browser.i18n.getMessage = () => 'fake-message';
|
||||
|
||||
const logMock = vi.fn();
|
||||
console.log = logMock;
|
||||
@@ -11,7 +11,7 @@ describe('Background Entrypoint', () => {
|
||||
fakeBrowser.reset();
|
||||
});
|
||||
|
||||
it("should log the extenion's runtime ID", () => {
|
||||
it("should log the extension's runtime ID", () => {
|
||||
const id = 'some-id';
|
||||
fakeBrowser.runtime.id = id;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { presetUno } from 'unocss';
|
||||
|
||||
export default defineConfig({
|
||||
srcDir: 'src',
|
||||
extensionApi: 'chrome',
|
||||
targetBrowsers: ['chrome', 'firefox', 'safari'],
|
||||
manifest: {
|
||||
permissions: ['storage'],
|
||||
default_locale: 'en',
|
||||
@@ -20,7 +20,7 @@ export default defineConfig({
|
||||
analysis: {
|
||||
open: true,
|
||||
},
|
||||
runner: {
|
||||
webExt: {
|
||||
startUrls: ['https://duckduckgo.com'],
|
||||
},
|
||||
example: {
|
||||
|
||||
@@ -1,5 +1,95 @@
|
||||
# Changelog
|
||||
|
||||
## v0.20.2
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.1...wxt-v0.20.2)
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Fix hashing issue with inline scripts ([#1591](https://github.com/wxt-dev/wxt/pull/1591))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Fix typo in changelog ([acb6cd1](https://github.com/wxt-dev/wxt/commit/acb6cd1))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Yunsup Sim ([@SimYunSup](https://github.com/SimYunSup))
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
|
||||
## v0.20.1
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.0...wxt-v0.20.1)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Enable wxt usage inside of devcontainers ([#1406](https://github.com/wxt-dev/wxt/pull/1406))
|
||||
- Type-safe `import.meta.env.BROWSER` with new `targetBrowsers` config ([#1574](https://github.com/wxt-dev/wxt/pull/1574))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Don't remove top-level destructured variable definitions when importing entrypoints ([#1561](https://github.com/wxt-dev/wxt/pull/1561))
|
||||
- Add JSDoc type annotation to auto-imports for ESlint ([#1558](https://github.com/wxt-dev/wxt/pull/1558))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Fix knowledge file generation ([#1550](https://github.com/wxt-dev/wxt/pull/1550))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **deps:** Update all dependencies ([#1568](https://github.com/wxt-dev/wxt/pull/1568))
|
||||
- Update comment ([61b42ef](https://github.com/wxt-dev/wxt/commit/61b42ef))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- 7sDream ([@7sDream](https://github.com/7sDream))
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
- Nostro ([@nostrorom](https://github.com/nostrorom))
|
||||
- Khalil Yao ([@yyz945947732](https://github.com/yyz945947732))
|
||||
- Alec WM ([@alecdwm](https://github.com/alecdwm))
|
||||
|
||||
## v0.20.0
|
||||
|
||||
Excited to release the next major version of WXT! Follow the [Upgrade guide](https://wxt.dev/guide/resources/upgrading.html) to update!
|
||||
|
||||
---
|
||||
|
||||
[⚠️ breaking changes](https://wxt.dev/guide/resources/upgrading.html) • [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.29...wxt-v0.20.0)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- ⚠️ Remove `webextension-polyfill` ([#1084](https://github.com/wxt-dev/wxt/pull/1084))
|
||||
- ⚠️ Individual exports and introduce the `#imports` module ([#1258](https://github.com/wxt-dev/wxt/pull/1258))
|
||||
- ⚠️ Reset inherited styles inside shadow root ([#1269](https://github.com/wxt-dev/wxt/pull/1269))
|
||||
- ⚠️ Auto-import types ([#1315](https://github.com/wxt-dev/wxt/pull/1315))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- ⚠️ Add suffix to non-production output directories ([#1086](https://github.com/wxt-dev/wxt/pull/1086))
|
||||
- ⚠️ Remove deprecated `jiti` entrypoint loader ([#1087](https://github.com/wxt-dev/wxt/pull/1087))
|
||||
- ⚠️ Rename `runner` to `webExt` ([#1180](https://github.com/wxt-dev/wxt/pull/1180))
|
||||
- ⚠️ Remove `transformManfiest` option ([#1181](https://github.com/wxt-dev/wxt/pull/1181))
|
||||
- Remove unnecessary `VITE_CJS_IGNORE_WARNING` flag ([b0ef178](https://github.com/wxt-dev/wxt/commit/b0ef178))
|
||||
- ⚠️ Make `publicDir` and `modulesDir` relative to project root ([#1216](https://github.com/wxt-dev/wxt/pull/1216))
|
||||
- ⚠️ Move `wxt/storage` to `wxt/utils/storage` ([#1271](https://github.com/wxt-dev/wxt/pull/1271))
|
||||
- Add back `ExtensionRunnerConfig` as deprecated ([#1311](https://github.com/wxt-dev/wxt/pull/1311))
|
||||
- Missing browser in shadow-root file ([#1317](https://github.com/wxt-dev/wxt/pull/1317))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Fix api reference for `wxt/utils/storage` ([99b5076](https://github.com/wxt-dev/wxt/commit/99b5076))
|
||||
- Fix broken links ([82d8024](https://github.com/wxt-dev/wxt/commit/82d8024))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Fix type errors ([aad17c8](https://github.com/wxt-dev/wxt/commit/aad17c8))
|
||||
- Remove duplicate test ([e54df0a](https://github.com/wxt-dev/wxt/commit/e54df0a))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
- 1natsu ([@1natsu172](https://github.com/1natsu172))
|
||||
|
||||
## v0.19.29
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.28...wxt-v0.19.29)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
import { version } from './package.json';
|
||||
import { version, exports } from './package.json';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import {
|
||||
virtualEntrypointModuleNames,
|
||||
@@ -32,10 +32,7 @@ export default defineBuildConfig([
|
||||
...virtualEntrypointModuleNames.map((name) => `virtual:user-${name}`),
|
||||
'virtual:wxt-plugins',
|
||||
'virtual:app-config',
|
||||
'wxt/browser',
|
||||
'wxt/sandbox',
|
||||
'wxt/client',
|
||||
'wxt/testing',
|
||||
...Object.keys(exports).map((path) => 'wxt' + path.slice(1)), // ./utils/storage => wxt/utils/storage
|
||||
],
|
||||
})),
|
||||
]);
|
||||
|
||||
@@ -5,17 +5,44 @@ exports[`Auto Imports > eslintrc > "enabled: 8" should output a JSON config file
|
||||
----------------------------------------
|
||||
{
|
||||
"globals": {
|
||||
"AutoMount": true,
|
||||
"AutoMountOptions": true,
|
||||
"Browser": true,
|
||||
"ContentScriptAnchoredOptions": true,
|
||||
"ContentScriptAppendMode": true,
|
||||
"ContentScriptContext": true,
|
||||
"ContentScriptInlinePositioningOptions": true,
|
||||
"ContentScriptModalPositioningOptions": true,
|
||||
"ContentScriptOverlayAlignment": true,
|
||||
"ContentScriptOverlayPositioningOptions": true,
|
||||
"ContentScriptPositioningOptions": true,
|
||||
"ContentScriptUi": true,
|
||||
"ContentScriptUiOptions": true,
|
||||
"IframeContentScriptUi": true,
|
||||
"IframeContentScriptUiOptions": true,
|
||||
"InjectScriptOptions": true,
|
||||
"IntegratedContentScriptUi": true,
|
||||
"IntegratedContentScriptUiOptions": true,
|
||||
"InvalidMatchPattern": true,
|
||||
"MatchPattern": true,
|
||||
"MigrationError": true,
|
||||
"ScriptPublicPath": true,
|
||||
"ShadowRootContentScriptUi": true,
|
||||
"ShadowRootContentScriptUiOptions": true,
|
||||
"StopAutoMount": true,
|
||||
"StorageArea": true,
|
||||
"StorageAreaChanges": true,
|
||||
"StorageItemKey": true,
|
||||
"WxtAppConfig": true,
|
||||
"WxtStorage": true,
|
||||
"WxtStorageItem": true,
|
||||
"WxtWindowEventMap": true,
|
||||
"browser": true,
|
||||
"createIframeUi": true,
|
||||
"createIntegratedUi": true,
|
||||
"createShadowRootUi": true,
|
||||
"defineAppConfig": true,
|
||||
"defineBackground": true,
|
||||
"defineConfig": true,
|
||||
"defineContentScript": true,
|
||||
"defineUnlistedScript": true,
|
||||
"defineWxtPlugin": true,
|
||||
@@ -32,17 +59,44 @@ exports[`Auto Imports > eslintrc > "enabled: 9" should output a flat config file
|
||||
".wxt/eslint-auto-imports.mjs
|
||||
----------------------------------------
|
||||
const globals = {
|
||||
"AutoMount": true,
|
||||
"AutoMountOptions": true,
|
||||
"Browser": true,
|
||||
"ContentScriptAnchoredOptions": true,
|
||||
"ContentScriptAppendMode": true,
|
||||
"ContentScriptContext": true,
|
||||
"ContentScriptInlinePositioningOptions": true,
|
||||
"ContentScriptModalPositioningOptions": true,
|
||||
"ContentScriptOverlayAlignment": true,
|
||||
"ContentScriptOverlayPositioningOptions": true,
|
||||
"ContentScriptPositioningOptions": true,
|
||||
"ContentScriptUi": true,
|
||||
"ContentScriptUiOptions": true,
|
||||
"IframeContentScriptUi": true,
|
||||
"IframeContentScriptUiOptions": true,
|
||||
"InjectScriptOptions": true,
|
||||
"IntegratedContentScriptUi": true,
|
||||
"IntegratedContentScriptUiOptions": true,
|
||||
"InvalidMatchPattern": true,
|
||||
"MatchPattern": true,
|
||||
"MigrationError": true,
|
||||
"ScriptPublicPath": true,
|
||||
"ShadowRootContentScriptUi": true,
|
||||
"ShadowRootContentScriptUiOptions": true,
|
||||
"StopAutoMount": true,
|
||||
"StorageArea": true,
|
||||
"StorageAreaChanges": true,
|
||||
"StorageItemKey": true,
|
||||
"WxtAppConfig": true,
|
||||
"WxtStorage": true,
|
||||
"WxtStorageItem": true,
|
||||
"WxtWindowEventMap": true,
|
||||
"browser": true,
|
||||
"createIframeUi": true,
|
||||
"createIntegratedUi": true,
|
||||
"createShadowRootUi": true,
|
||||
"defineAppConfig": true,
|
||||
"defineBackground": true,
|
||||
"defineConfig": true,
|
||||
"defineContentScript": true,
|
||||
"defineUnlistedScript": true,
|
||||
"defineWxtPlugin": true,
|
||||
@@ -56,6 +110,7 @@ export default {
|
||||
name: "wxt/auto-imports",
|
||||
languageOptions: {
|
||||
globals,
|
||||
/** @type {import('eslint').Linter.SourceType} */
|
||||
sourceType: "module",
|
||||
},
|
||||
};
|
||||
@@ -67,17 +122,44 @@ exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config f
|
||||
----------------------------------------
|
||||
{
|
||||
"globals": {
|
||||
"AutoMount": true,
|
||||
"AutoMountOptions": true,
|
||||
"Browser": true,
|
||||
"ContentScriptAnchoredOptions": true,
|
||||
"ContentScriptAppendMode": true,
|
||||
"ContentScriptContext": true,
|
||||
"ContentScriptInlinePositioningOptions": true,
|
||||
"ContentScriptModalPositioningOptions": true,
|
||||
"ContentScriptOverlayAlignment": true,
|
||||
"ContentScriptOverlayPositioningOptions": true,
|
||||
"ContentScriptPositioningOptions": true,
|
||||
"ContentScriptUi": true,
|
||||
"ContentScriptUiOptions": true,
|
||||
"IframeContentScriptUi": true,
|
||||
"IframeContentScriptUiOptions": true,
|
||||
"InjectScriptOptions": true,
|
||||
"IntegratedContentScriptUi": true,
|
||||
"IntegratedContentScriptUiOptions": true,
|
||||
"InvalidMatchPattern": true,
|
||||
"MatchPattern": true,
|
||||
"MigrationError": true,
|
||||
"ScriptPublicPath": true,
|
||||
"ShadowRootContentScriptUi": true,
|
||||
"ShadowRootContentScriptUiOptions": true,
|
||||
"StopAutoMount": true,
|
||||
"StorageArea": true,
|
||||
"StorageAreaChanges": true,
|
||||
"StorageItemKey": true,
|
||||
"WxtAppConfig": true,
|
||||
"WxtStorage": true,
|
||||
"WxtStorageItem": true,
|
||||
"WxtWindowEventMap": true,
|
||||
"browser": true,
|
||||
"createIframeUi": true,
|
||||
"createIntegratedUi": true,
|
||||
"createShadowRootUi": true,
|
||||
"defineAppConfig": true,
|
||||
"defineBackground": true,
|
||||
"defineConfig": true,
|
||||
"defineContentScript": true,
|
||||
"defineUnlistedScript": true,
|
||||
"defineWxtPlugin": true,
|
||||
@@ -95,17 +177,44 @@ exports[`Auto Imports > eslintrc > should allow customizing the output 1`] = `
|
||||
----------------------------------------
|
||||
{
|
||||
"globals": {
|
||||
"AutoMount": "readonly",
|
||||
"AutoMountOptions": "readonly",
|
||||
"Browser": "readonly",
|
||||
"ContentScriptAnchoredOptions": "readonly",
|
||||
"ContentScriptAppendMode": "readonly",
|
||||
"ContentScriptContext": "readonly",
|
||||
"ContentScriptInlinePositioningOptions": "readonly",
|
||||
"ContentScriptModalPositioningOptions": "readonly",
|
||||
"ContentScriptOverlayAlignment": "readonly",
|
||||
"ContentScriptOverlayPositioningOptions": "readonly",
|
||||
"ContentScriptPositioningOptions": "readonly",
|
||||
"ContentScriptUi": "readonly",
|
||||
"ContentScriptUiOptions": "readonly",
|
||||
"IframeContentScriptUi": "readonly",
|
||||
"IframeContentScriptUiOptions": "readonly",
|
||||
"InjectScriptOptions": "readonly",
|
||||
"IntegratedContentScriptUi": "readonly",
|
||||
"IntegratedContentScriptUiOptions": "readonly",
|
||||
"InvalidMatchPattern": "readonly",
|
||||
"MatchPattern": "readonly",
|
||||
"MigrationError": "readonly",
|
||||
"ScriptPublicPath": "readonly",
|
||||
"ShadowRootContentScriptUi": "readonly",
|
||||
"ShadowRootContentScriptUiOptions": "readonly",
|
||||
"StopAutoMount": "readonly",
|
||||
"StorageArea": "readonly",
|
||||
"StorageAreaChanges": "readonly",
|
||||
"StorageItemKey": "readonly",
|
||||
"WxtAppConfig": "readonly",
|
||||
"WxtStorage": "readonly",
|
||||
"WxtStorageItem": "readonly",
|
||||
"WxtWindowEventMap": "readonly",
|
||||
"browser": "readonly",
|
||||
"createIframeUi": "readonly",
|
||||
"createIntegratedUi": "readonly",
|
||||
"createShadowRootUi": "readonly",
|
||||
"defineAppConfig": "readonly",
|
||||
"defineBackground": "readonly",
|
||||
"defineConfig": "readonly",
|
||||
"defineContentScript": "readonly",
|
||||
"defineUnlistedScript": "readonly",
|
||||
"defineWxtPlugin": "readonly",
|
||||
|
||||
@@ -17,24 +17,52 @@ describe('Auto Imports', () => {
|
||||
// Generated by wxt
|
||||
export {}
|
||||
declare global {
|
||||
const ContentScriptContext: typeof import('wxt/client')['ContentScriptContext']
|
||||
const InvalidMatchPattern: typeof import('wxt/sandbox')['InvalidMatchPattern']
|
||||
const MatchPattern: typeof import('wxt/sandbox')['MatchPattern']
|
||||
const MigrationError: typeof import('wxt/storage')['MigrationError']
|
||||
const ContentScriptContext: typeof import('wxt/utils/content-script-context')['ContentScriptContext']
|
||||
const InvalidMatchPattern: typeof import('wxt/utils/match-patterns')['InvalidMatchPattern']
|
||||
const MatchPattern: typeof import('wxt/utils/match-patterns')['MatchPattern']
|
||||
const browser: typeof import('wxt/browser')['browser']
|
||||
const createIframeUi: typeof import('wxt/client')['createIframeUi']
|
||||
const createIntegratedUi: typeof import('wxt/client')['createIntegratedUi']
|
||||
const createShadowRootUi: typeof import('wxt/client')['createShadowRootUi']
|
||||
const defineAppConfig: typeof import('wxt/sandbox')['defineAppConfig']
|
||||
const defineBackground: typeof import('wxt/sandbox')['defineBackground']
|
||||
const defineConfig: typeof import('wxt')['defineConfig']
|
||||
const defineContentScript: typeof import('wxt/sandbox')['defineContentScript']
|
||||
const defineUnlistedScript: typeof import('wxt/sandbox')['defineUnlistedScript']
|
||||
const defineWxtPlugin: typeof import('wxt/sandbox')['defineWxtPlugin']
|
||||
const createIframeUi: typeof import('wxt/utils/content-script-ui/iframe')['createIframeUi']
|
||||
const createIntegratedUi: typeof import('wxt/utils/content-script-ui/integrated')['createIntegratedUi']
|
||||
const createShadowRootUi: typeof import('wxt/utils/content-script-ui/shadow-root')['createShadowRootUi']
|
||||
const defineAppConfig: typeof import('wxt/utils/define-app-config')['defineAppConfig']
|
||||
const defineBackground: typeof import('wxt/utils/define-background')['defineBackground']
|
||||
const defineContentScript: typeof import('wxt/utils/define-content-script')['defineContentScript']
|
||||
const defineUnlistedScript: typeof import('wxt/utils/define-unlisted-script')['defineUnlistedScript']
|
||||
const defineWxtPlugin: typeof import('wxt/utils/define-wxt-plugin')['defineWxtPlugin']
|
||||
const fakeBrowser: typeof import('wxt/testing')['fakeBrowser']
|
||||
const injectScript: typeof import('wxt/client')['injectScript']
|
||||
const storage: typeof import('wxt/storage')['storage']
|
||||
const useAppConfig: typeof import('wxt/client')['useAppConfig']
|
||||
const injectScript: typeof import('wxt/utils/inject-script')['injectScript']
|
||||
const storage: typeof import('wxt/utils/storage')['storage']
|
||||
const useAppConfig: typeof import('wxt/utils/app-config')['useAppConfig']
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Browser } from 'wxt/browser'
|
||||
import('wxt/browser')
|
||||
// @ts-ignore
|
||||
export type { StorageArea, WxtStorage, WxtStorageItem, StorageItemKey, StorageAreaChanges, MigrationError } from 'wxt/utils/storage'
|
||||
import('wxt/utils/storage')
|
||||
// @ts-ignore
|
||||
export type { WxtWindowEventMap } from 'wxt/utils/content-script-context'
|
||||
import('wxt/utils/content-script-context')
|
||||
// @ts-ignore
|
||||
export type { IframeContentScriptUi, IframeContentScriptUiOptions } from 'wxt/utils/content-script-ui/iframe'
|
||||
import('wxt/utils/content-script-ui/iframe')
|
||||
// @ts-ignore
|
||||
export type { IntegratedContentScriptUi, IntegratedContentScriptUiOptions } from 'wxt/utils/content-script-ui/integrated'
|
||||
import('wxt/utils/content-script-ui/integrated')
|
||||
// @ts-ignore
|
||||
export type { ShadowRootContentScriptUi, ShadowRootContentScriptUiOptions } from 'wxt/utils/content-script-ui/shadow-root'
|
||||
import('wxt/utils/content-script-ui/shadow-root')
|
||||
// @ts-ignore
|
||||
export type { ContentScriptUi, ContentScriptUiOptions, ContentScriptOverlayAlignment, ContentScriptAppendMode, ContentScriptInlinePositioningOptions, ContentScriptOverlayPositioningOptions, ContentScriptModalPositioningOptions, ContentScriptPositioningOptions, ContentScriptAnchoredOptions, AutoMountOptions, StopAutoMount, AutoMount } from 'wxt/utils/content-script-ui/types'
|
||||
import('wxt/utils/content-script-ui/types')
|
||||
// @ts-ignore
|
||||
export type { WxtAppConfig } from 'wxt/utils/define-app-config'
|
||||
import('wxt/utils/define-app-config')
|
||||
// @ts-ignore
|
||||
export type { ScriptPublicPath, InjectScriptOptions } from 'wxt/utils/inject-script'
|
||||
import('wxt/utils/inject-script')
|
||||
}
|
||||
"
|
||||
`);
|
||||
@@ -55,10 +83,53 @@ describe('Auto Imports', () => {
|
||||
/// <reference path="./types/paths.d.ts" />
|
||||
/// <reference path="./types/i18n.d.ts" />
|
||||
/// <reference path="./types/globals.d.ts" />
|
||||
/// <reference path="./types/imports-module.d.ts" />
|
||||
/// <reference path="./types/imports.d.ts" />
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should generate the #imports module', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile('entrypoints/popup.html', `<html></html>`);
|
||||
// Project auto-imports should also be present
|
||||
project.addFile(
|
||||
'utils/time.ts',
|
||||
`export function startOfDay(date: Date): Date {
|
||||
throw Error("TODO")
|
||||
}`,
|
||||
);
|
||||
|
||||
await project.prepare();
|
||||
|
||||
expect(await project.serializeFile('.wxt/types/imports-module.d.ts'))
|
||||
.toMatchInlineSnapshot(`
|
||||
".wxt/types/imports-module.d.ts
|
||||
----------------------------------------
|
||||
// Generated by wxt
|
||||
// Types for the #import virtual module
|
||||
declare module '#imports' {
|
||||
export { browser, Browser } from 'wxt/browser';
|
||||
export { storage, StorageArea, WxtStorage, WxtStorageItem, StorageItemKey, StorageAreaChanges, MigrationError } from 'wxt/utils/storage';
|
||||
export { useAppConfig } from 'wxt/utils/app-config';
|
||||
export { ContentScriptContext, WxtWindowEventMap } from 'wxt/utils/content-script-context';
|
||||
export { createIframeUi, IframeContentScriptUi, IframeContentScriptUiOptions } from 'wxt/utils/content-script-ui/iframe';
|
||||
export { createIntegratedUi, IntegratedContentScriptUi, IntegratedContentScriptUiOptions } from 'wxt/utils/content-script-ui/integrated';
|
||||
export { createShadowRootUi, ShadowRootContentScriptUi, ShadowRootContentScriptUiOptions } from 'wxt/utils/content-script-ui/shadow-root';
|
||||
export { ContentScriptUi, ContentScriptUiOptions, ContentScriptOverlayAlignment, ContentScriptAppendMode, ContentScriptInlinePositioningOptions, ContentScriptOverlayPositioningOptions, ContentScriptModalPositioningOptions, ContentScriptPositioningOptions, ContentScriptAnchoredOptions, AutoMountOptions, StopAutoMount, AutoMount } from 'wxt/utils/content-script-ui/types';
|
||||
export { defineAppConfig, WxtAppConfig } from 'wxt/utils/define-app-config';
|
||||
export { defineBackground } from 'wxt/utils/define-background';
|
||||
export { defineContentScript } from 'wxt/utils/define-content-script';
|
||||
export { defineUnlistedScript } from 'wxt/utils/define-unlisted-script';
|
||||
export { defineWxtPlugin } from 'wxt/utils/define-wxt-plugin';
|
||||
export { injectScript, ScriptPublicPath, InjectScriptOptions } from 'wxt/utils/inject-script';
|
||||
export { InvalidMatchPattern, MatchPattern } from 'wxt/utils/match-patterns';
|
||||
export { fakeBrowser } from 'wxt/testing';
|
||||
export { startOfDay } from '../utils/time';
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('imports: false', () => {
|
||||
@@ -74,7 +145,7 @@ describe('Auto Imports', () => {
|
||||
expect(await project.fileExists('.wxt/types/imports.d.ts')).toBe(false);
|
||||
});
|
||||
|
||||
it('should not include imports.d.ts in the type references', async () => {
|
||||
it('should only include imports-module.d.ts in the the project', async () => {
|
||||
const project = new TestProject();
|
||||
project.setConfigFileConfig({
|
||||
imports: false,
|
||||
@@ -94,10 +165,55 @@ describe('Auto Imports', () => {
|
||||
/// <reference path="./types/paths.d.ts" />
|
||||
/// <reference path="./types/i18n.d.ts" />
|
||||
/// <reference path="./types/globals.d.ts" />
|
||||
/// <reference path="./types/imports-module.d.ts" />
|
||||
"
|
||||
`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should only generate the #imports module', async () => {
|
||||
const project = new TestProject();
|
||||
project.setConfigFileConfig({
|
||||
imports: false,
|
||||
});
|
||||
project.addFile('entrypoints/popup.html', `<html></html>`);
|
||||
// Project auto-imports should also be present
|
||||
project.addFile(
|
||||
'utils/time.ts',
|
||||
`export function startOfDay(date: Date): Date {
|
||||
throw Error("TODO")
|
||||
}`,
|
||||
);
|
||||
|
||||
await project.prepare();
|
||||
|
||||
expect(await project.serializeFile('.wxt/types/imports-module.d.ts'))
|
||||
.toMatchInlineSnapshot(`
|
||||
".wxt/types/imports-module.d.ts
|
||||
----------------------------------------
|
||||
// Generated by wxt
|
||||
// Types for the #import virtual module
|
||||
declare module '#imports' {
|
||||
export { browser, Browser } from 'wxt/browser';
|
||||
export { storage, StorageArea, WxtStorage, WxtStorageItem, StorageItemKey, StorageAreaChanges, MigrationError } from 'wxt/utils/storage';
|
||||
export { useAppConfig } from 'wxt/utils/app-config';
|
||||
export { ContentScriptContext, WxtWindowEventMap } from 'wxt/utils/content-script-context';
|
||||
export { createIframeUi, IframeContentScriptUi, IframeContentScriptUiOptions } from 'wxt/utils/content-script-ui/iframe';
|
||||
export { createIntegratedUi, IntegratedContentScriptUi, IntegratedContentScriptUiOptions } from 'wxt/utils/content-script-ui/integrated';
|
||||
export { createShadowRootUi, ShadowRootContentScriptUi, ShadowRootContentScriptUiOptions } from 'wxt/utils/content-script-ui/shadow-root';
|
||||
export { ContentScriptUi, ContentScriptUiOptions, ContentScriptOverlayAlignment, ContentScriptAppendMode, ContentScriptInlinePositioningOptions, ContentScriptOverlayPositioningOptions, ContentScriptModalPositioningOptions, ContentScriptPositioningOptions, ContentScriptAnchoredOptions, AutoMountOptions, StopAutoMount, AutoMount } from 'wxt/utils/content-script-ui/types';
|
||||
export { defineAppConfig, WxtAppConfig } from 'wxt/utils/define-app-config';
|
||||
export { defineBackground } from 'wxt/utils/define-background';
|
||||
export { defineContentScript } from 'wxt/utils/define-content-script';
|
||||
export { defineUnlistedScript } from 'wxt/utils/define-unlisted-script';
|
||||
export { defineWxtPlugin } from 'wxt/utils/define-wxt-plugin';
|
||||
export { injectScript, ScriptPublicPath, InjectScriptOptions } from 'wxt/utils/inject-script';
|
||||
export { InvalidMatchPattern, MatchPattern } from 'wxt/utils/match-patterns';
|
||||
export { fakeBrowser } from 'wxt/testing';
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('eslintrc', () => {
|
||||
|
||||
@@ -180,7 +180,7 @@ describe('Hooks', () => {
|
||||
|
||||
const server = await project.startServer({
|
||||
hooks,
|
||||
runner: {
|
||||
webExt: {
|
||||
disabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { TestProject } from '../utils';
|
||||
|
||||
describe.each(['vite-node', 'jiti'] as const)(
|
||||
'Manifest Content (Vite runtime? %s)',
|
||||
(entrypointImporter) => {
|
||||
it.each([
|
||||
{ browser: undefined, outDir: 'chrome-mv3', expected: undefined },
|
||||
{ browser: 'chrome', outDir: 'chrome-mv3', expected: undefined },
|
||||
{ browser: 'firefox', outDir: 'firefox-mv2', expected: true },
|
||||
{ browser: 'safari', outDir: 'safari-mv2', expected: false },
|
||||
])(
|
||||
'should respect the per-browser entrypoint option with %j',
|
||||
async ({ browser, expected, outDir }) => {
|
||||
const project = new TestProject();
|
||||
describe('Manifest Content', () => {
|
||||
it.each([
|
||||
{ browser: undefined, outDir: 'chrome-mv3', expected: undefined },
|
||||
{ browser: 'chrome', outDir: 'chrome-mv3', expected: undefined },
|
||||
{ browser: 'firefox', outDir: 'firefox-mv2', expected: true },
|
||||
{ browser: 'safari', outDir: 'safari-mv2', expected: false },
|
||||
])(
|
||||
'should respect the per-browser entrypoint option with %j',
|
||||
async ({ browser, expected, outDir }) => {
|
||||
const project = new TestProject();
|
||||
|
||||
project.addFile(
|
||||
'entrypoints/background.ts',
|
||||
`export default defineBackground({
|
||||
project.addFile(
|
||||
'entrypoints/background.ts',
|
||||
`export default defineBackground({
|
||||
persistent: {
|
||||
firefox: true,
|
||||
safari: false,
|
||||
},
|
||||
main: () => {},
|
||||
})`,
|
||||
);
|
||||
await project.build({ browser, experimental: { entrypointImporter } });
|
||||
);
|
||||
await project.build({ browser });
|
||||
|
||||
const safariManifest = await project.getOutputManifest(
|
||||
`.output/${outDir}/manifest.json`,
|
||||
);
|
||||
expect(safariManifest.background.persistent).toBe(expected);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
const safariManifest = await project.getOutputManifest(
|
||||
`.output/${outDir}/manifest.json`,
|
||||
);
|
||||
expect(safariManifest.background.persistent).toBe(expected);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -191,10 +191,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
@@ -211,10 +208,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
@@ -232,10 +226,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
@@ -248,10 +239,7 @@ describe('Module Helpers', () => {
|
||||
);
|
||||
const expectedText = addPluginModule(project);
|
||||
|
||||
await project.build({
|
||||
// reduce build output when comparing test failures
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
await project.build();
|
||||
|
||||
await expect(project.serializeOutput()).resolves.toContain(expectedText);
|
||||
});
|
||||
|
||||
@@ -262,9 +262,6 @@ describe('Output Directory Structure', () => {
|
||||
project.addFile('entrypoints/popup/main.ts', `logHello('popup')`);
|
||||
|
||||
await project.build({
|
||||
// Simplify the build output for comparison
|
||||
extensionApi: 'chrome',
|
||||
|
||||
vite: () => ({
|
||||
build: {
|
||||
// Make output for snapshot readible
|
||||
@@ -289,11 +286,7 @@ describe('Output Directory Structure', () => {
|
||||
logHello("background");
|
||||
}
|
||||
});
|
||||
// @ts-expect-error
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) == null ? globalThis.chrome : (
|
||||
// @ts-expect-error
|
||||
globalThis.browser
|
||||
);
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) ? globalThis.browser : globalThis.chrome;
|
||||
function print(method, ...args) {
|
||||
return;
|
||||
}
|
||||
@@ -347,9 +340,6 @@ describe('Output Directory Structure', () => {
|
||||
project.addFile('entrypoints/popup/main.ts', `logHello('popup')`);
|
||||
|
||||
await project.build({
|
||||
// Simplify the build output for comparison
|
||||
extensionApi: 'chrome',
|
||||
|
||||
vite: () => ({
|
||||
build: {
|
||||
// Make output for snapshot readible
|
||||
@@ -381,11 +371,7 @@ describe('Output Directory Structure', () => {
|
||||
background;
|
||||
function initPlugins() {
|
||||
}
|
||||
// @ts-expect-error
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) == null ? globalThis.chrome : (
|
||||
// @ts-expect-error
|
||||
globalThis.browser
|
||||
);
|
||||
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) ? globalThis.browser : globalThis.chrome;
|
||||
function print(method, ...args) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@ describe('TypeScript Project', () => {
|
||||
/// <reference path="./types/paths.d.ts" />
|
||||
/// <reference path="./types/i18n.d.ts" />
|
||||
/// <reference path="./types/globals.d.ts" />
|
||||
/// <reference path="./types/imports-module.d.ts" />
|
||||
/// <reference path="./types/imports.d.ts" />
|
||||
"
|
||||
`);
|
||||
@@ -395,6 +396,19 @@ describe('TypeScript Project', () => {
|
||||
expect(output).toContain('./example.ts');
|
||||
});
|
||||
|
||||
it('should set correct import.meta.env.BROWSER type based on targetBrowsers', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile('entrypoints/unlisted.html', '<html></html>');
|
||||
project.setConfigFileConfig({
|
||||
targetBrowsers: ['firefox', 'chrome'],
|
||||
});
|
||||
|
||||
await project.prepare();
|
||||
|
||||
const output = await project.serializeFile('.wxt/types/globals.d.ts');
|
||||
expect(output).toContain('readonly BROWSER: "firefox" | "chrome";');
|
||||
});
|
||||
|
||||
// TODO: Once a module has been published, use it here for testing - local files are never added to the .wxt/wxt.d.ts file
|
||||
it.todo(
|
||||
'should add modules from NPM to the TS project if they have a configKey',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { TestProject } from '../utils';
|
||||
import { InlineConfig } from '../../src/types';
|
||||
|
||||
describe('User Config', () => {
|
||||
// Root directory is tested with all tests.
|
||||
@@ -88,24 +87,6 @@ describe('User Config', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should exclude the polyfill when extensionApi="chrome"', async () => {
|
||||
const buildBackground = async (config?: InlineConfig) => {
|
||||
const background = `export default defineBackground(() => console.log(browser.runtime.id));`;
|
||||
const projectWithPolyfill = new TestProject();
|
||||
projectWithPolyfill.addFile('entrypoints/background.ts', background);
|
||||
await projectWithPolyfill.build(config);
|
||||
return await projectWithPolyfill.serializeFile(
|
||||
'.output/chrome-mv3/background.js',
|
||||
);
|
||||
};
|
||||
|
||||
const withPolyfill = await buildBackground();
|
||||
const withoutPolyfill = await buildBackground({
|
||||
extensionApi: 'chrome',
|
||||
});
|
||||
expect(withoutPolyfill).not.toBe(withPolyfill);
|
||||
});
|
||||
|
||||
it('should respect changing config files', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile(
|
||||
|
||||
+108
-75
@@ -1,75 +1,9 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.19.29",
|
||||
"version": "0.20.2",
|
||||
"description": "⚡ Next-gen Web Extension Framework",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wxt-dev/wxt.git"
|
||||
},
|
||||
"homepage": "https://wxt.dev",
|
||||
"keywords": [
|
||||
"vite",
|
||||
"chrome",
|
||||
"web",
|
||||
"extension",
|
||||
"browser",
|
||||
"bundler",
|
||||
"framework"
|
||||
],
|
||||
"author": {
|
||||
"name": "Aaron Klinker",
|
||||
"email": "aaronklinker1+wxt@gmail.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"funding": "https://github.com/sponsors/wxt-dev",
|
||||
"files": [
|
||||
"bin",
|
||||
"dist"
|
||||
],
|
||||
"bin": {
|
||||
"wxt": "./bin/wxt.mjs",
|
||||
"wxt-publish-extension": "./bin/wxt-publish-extension.cjs"
|
||||
},
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"./client": {
|
||||
"types": "./dist/client/index.d.ts",
|
||||
"default": "./dist/client/index.mjs"
|
||||
},
|
||||
"./sandbox": {
|
||||
"types": "./dist/sandbox/index.d.ts",
|
||||
"default": "./dist/sandbox/index.mjs"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./dist/browser/index.d.ts",
|
||||
"default": "./dist/browser/index.mjs"
|
||||
},
|
||||
"./browser/chrome": {
|
||||
"types": "./dist/browser/chrome.d.ts",
|
||||
"import": "./dist/browser/chrome.mjs"
|
||||
},
|
||||
"./testing": {
|
||||
"types": "./dist/testing/index.d.ts",
|
||||
"default": "./dist/testing/index.mjs"
|
||||
},
|
||||
"./storage": {
|
||||
"types": "./dist/storage.d.ts",
|
||||
"default": "./dist/storage.mjs"
|
||||
},
|
||||
"./vite-builder-env": {
|
||||
"types": "./dist/vite-builder-env.d.ts"
|
||||
},
|
||||
"./modules": {
|
||||
"types": "./dist/modules.d.ts",
|
||||
"default": "./dist/modules.mjs"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"wxt": "tsx src/cli/index.ts",
|
||||
"build": "buildc -- unbuild",
|
||||
@@ -84,11 +18,10 @@
|
||||
"dependencies": {
|
||||
"@1natsu/wait-element": "catalog:",
|
||||
"@aklinker1/rollup-plugin-visualizer": "catalog:",
|
||||
"@types/chrome": "catalog:",
|
||||
"@types/webextension-polyfill": "catalog:",
|
||||
"@webext-core/fake-browser": "catalog:",
|
||||
"@webext-core/isolated-element": "catalog:",
|
||||
"@webext-core/match-patterns": "catalog:",
|
||||
"@wxt-dev/browser": "workspace:*",
|
||||
"@wxt-dev/storage": "workspace:^1.0.0",
|
||||
"async-mutex": "catalog:",
|
||||
"c12": "catalog:",
|
||||
@@ -108,7 +41,6 @@
|
||||
"hookable": "catalog:",
|
||||
"import-meta-resolve": "catalog:",
|
||||
"is-wsl": "catalog:",
|
||||
"jiti": "catalog:",
|
||||
"json5": "catalog:",
|
||||
"jszip": "catalog:",
|
||||
"linkedom": "catalog:",
|
||||
@@ -128,8 +60,7 @@
|
||||
"unimport": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-node": "catalog:",
|
||||
"web-ext-run": "catalog:",
|
||||
"webextension-polyfill": "catalog:"
|
||||
"web-ext-run": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
@@ -149,9 +80,111 @@
|
||||
"vitest": "catalog:",
|
||||
"vitest-plugin-random-seed": "catalog:"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/chrome": {
|
||||
"optional": true
|
||||
"peerDependenciesMeta": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wxt-dev/wxt.git"
|
||||
},
|
||||
"homepage": "https://wxt.dev",
|
||||
"keywords": [
|
||||
"vite",
|
||||
"chrome",
|
||||
"web",
|
||||
"extension",
|
||||
"browser",
|
||||
"bundler",
|
||||
"framework"
|
||||
],
|
||||
"author": {
|
||||
"name": "Aaron Klinker",
|
||||
"email": "aaronklinker1+wxt@gmail.com"
|
||||
},
|
||||
"funding": "https://github.com/sponsors/wxt-dev",
|
||||
"files": [
|
||||
"bin",
|
||||
"dist"
|
||||
],
|
||||
"bin": {
|
||||
"wxt": "./bin/wxt.mjs",
|
||||
"wxt-publish-extension": "./bin/wxt-publish-extension.cjs"
|
||||
},
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"./utils/app-config": {
|
||||
"types": "./dist/utils/app-config.d.ts",
|
||||
"default": "./dist/utils/app-config.mjs"
|
||||
},
|
||||
"./utils/inject-script": {
|
||||
"types": "./dist/utils/inject-script.d.ts",
|
||||
"default": "./dist/utils/inject-script.mjs"
|
||||
},
|
||||
"./utils/content-script-context": {
|
||||
"types": "./dist/utils/content-script-context.d.ts",
|
||||
"default": "./dist/utils/content-script-context.mjs"
|
||||
},
|
||||
"./utils/content-script-ui/types": {
|
||||
"types": "./dist/utils/content-script-ui/types.d.ts",
|
||||
"default": "./dist/utils/content-script-ui/types.mjs"
|
||||
},
|
||||
"./utils/content-script-ui/integrated": {
|
||||
"types": "./dist/utils/content-script-ui/integrated.d.ts",
|
||||
"default": "./dist/utils/content-script-ui/integrated.mjs"
|
||||
},
|
||||
"./utils/content-script-ui/shadow-root": {
|
||||
"types": "./dist/utils/content-script-ui/shadow-root.d.ts",
|
||||
"default": "./dist/utils/content-script-ui/shadow-root.mjs"
|
||||
},
|
||||
"./utils/content-script-ui/iframe": {
|
||||
"types": "./dist/utils/content-script-ui/iframe.d.ts",
|
||||
"default": "./dist/utils/content-script-ui/iframe.mjs"
|
||||
},
|
||||
"./utils/define-app-config": {
|
||||
"types": "./dist/utils/define-app-config.d.ts",
|
||||
"default": "./dist/utils/define-app-config.mjs"
|
||||
},
|
||||
"./utils/define-background": {
|
||||
"types": "./dist/utils/define-background.d.ts",
|
||||
"default": "./dist/utils/define-background.mjs"
|
||||
},
|
||||
"./utils/define-content-script": {
|
||||
"types": "./dist/utils/define-content-script.d.ts",
|
||||
"default": "./dist/utils/define-content-script.mjs"
|
||||
},
|
||||
"./utils/define-unlisted-script": {
|
||||
"types": "./dist/utils/define-unlisted-script.d.ts",
|
||||
"default": "./dist/utils/define-unlisted-script.mjs"
|
||||
},
|
||||
"./utils/define-wxt-plugin": {
|
||||
"types": "./dist/utils/define-wxt-plugin.d.ts",
|
||||
"default": "./dist/utils/define-wxt-plugin.mjs"
|
||||
},
|
||||
"./utils/match-patterns": {
|
||||
"types": "./dist/utils/match-patterns.d.ts",
|
||||
"default": "./dist/utils/match-patterns.mjs"
|
||||
},
|
||||
"./utils/storage": {
|
||||
"types": "./dist/utils/storage.d.ts",
|
||||
"default": "./dist/utils/storage.mjs"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./dist/browser.d.ts",
|
||||
"default": "./dist/browser.mjs"
|
||||
},
|
||||
"./testing": {
|
||||
"types": "./dist/testing/index.d.ts",
|
||||
"default": "./dist/testing/index.mjs"
|
||||
},
|
||||
"./vite-builder-env": {
|
||||
"types": "./dist/vite-builder-env.d.ts"
|
||||
},
|
||||
"./modules": {
|
||||
"types": "./dist/modules.d.ts",
|
||||
"default": "./dist/modules.mjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-3
@@ -1,6 +1,4 @@
|
||||
declare const __DEV_SERVER_PROTOCOL__: string;
|
||||
declare const __DEV_SERVER_HOSTNAME__: string;
|
||||
declare const __DEV_SERVER_PORT__: string;
|
||||
declare const __DEV_SERVER_ORIGIN__: string;
|
||||
|
||||
// Globals defined by the vite-plugins/devServerGlobals.ts and utils/globals.ts
|
||||
interface ImportMetaEnv {
|
||||
|
||||
@@ -67,20 +67,5 @@ describe('Module Utilities', () => {
|
||||
|
||||
expect(wxt.config.imports && wxt.config.imports.presets).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should not enable imports if they've been disabled", async () => {
|
||||
const preset = 'vue';
|
||||
const wxt = fakeWxt({
|
||||
hooks: createHooks(),
|
||||
config: {
|
||||
imports: false,
|
||||
},
|
||||
});
|
||||
|
||||
addImportPreset(wxt, preset);
|
||||
await wxt.hooks.callHook('config:resolved', wxt);
|
||||
|
||||
expect(wxt.config.imports).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Contains the `browser` export which you should use to access the extension APIs in your project:
|
||||
* ```ts
|
||||
* import { browser } from 'wxt/browser';
|
||||
*
|
||||
* browser.runtime.onInstalled.addListener(() => {
|
||||
* // ...
|
||||
* })
|
||||
* ```
|
||||
* @module wxt/browser
|
||||
*/
|
||||
import { browser as _browser, type Browser } from '@wxt-dev/browser';
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/paths.d.ts`
|
||||
*/
|
||||
export interface WxtRuntime {}
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/i18n.d.ts`
|
||||
*/
|
||||
export interface WxtI18n {}
|
||||
|
||||
export type WxtBrowser = Omit<typeof _browser, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<(typeof _browser)['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<(typeof _browser)['i18n'], 'getMessage'>;
|
||||
};
|
||||
|
||||
export const browser: WxtBrowser = _browser;
|
||||
|
||||
export { Browser };
|
||||
@@ -1,23 +0,0 @@
|
||||
/// <reference types="chrome" />
|
||||
/**
|
||||
* EXPERIMENTAL
|
||||
*
|
||||
* Includes the `chrome` API and types when using `extensionApi: 'chrome'`.
|
||||
*
|
||||
* @module wxt/browser/chrome
|
||||
*/
|
||||
import type { WxtRuntime, WxtI18n } from './index';
|
||||
|
||||
export type WxtBrowser = Omit<typeof chrome, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<(typeof chrome)['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<(typeof chrome)['i18n'], 'getMessage'>;
|
||||
};
|
||||
|
||||
// #region snippet
|
||||
export const browser: WxtBrowser =
|
||||
// @ts-expect-error
|
||||
globalThis.browser?.runtime?.id == null
|
||||
? globalThis.chrome
|
||||
: // @ts-expect-error
|
||||
globalThis.browser;
|
||||
// #endregion snippet
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Includes the `browser` API and types when using `extensionApi: 'webextension-polyfill'` (the default).
|
||||
*
|
||||
* @module wxt/browser
|
||||
*/
|
||||
|
||||
import originalBrowser, { Browser } from 'webextension-polyfill';
|
||||
|
||||
export type AugmentedBrowser = Omit<Browser, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<Browser['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<Browser['i18n'], 'getMessage'>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/paths.d.ts`
|
||||
*/
|
||||
export interface WxtRuntime {}
|
||||
|
||||
/**
|
||||
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
|
||||
* - `.wxt/types/i18n.d.ts`
|
||||
*/
|
||||
export interface WxtI18n {}
|
||||
|
||||
export const browser: AugmentedBrowser = originalBrowser;
|
||||
|
||||
// re-export all the types from webextension-polyfill
|
||||
// Because webextension-polyfill uses a weird namespace with "import export", there isn't a good way
|
||||
// to get these types without re-listing them.
|
||||
/** @ignore */
|
||||
export type {
|
||||
ActivityLog,
|
||||
Alarms,
|
||||
Bookmarks,
|
||||
Action,
|
||||
BrowserAction,
|
||||
BrowserSettings,
|
||||
BrowsingData,
|
||||
CaptivePortal,
|
||||
Clipboard,
|
||||
Commands,
|
||||
ContentScripts,
|
||||
ContextualIdentities,
|
||||
Cookies,
|
||||
DeclarativeNetRequest,
|
||||
Devtools,
|
||||
Dns,
|
||||
Downloads,
|
||||
Events,
|
||||
Experiments,
|
||||
Extension,
|
||||
ExtensionTypes,
|
||||
Find,
|
||||
GeckoProfiler,
|
||||
History,
|
||||
I18n,
|
||||
Identity,
|
||||
Idle,
|
||||
Management,
|
||||
Manifest, // TODO: Export custom manifest types that are valid for both Chrome and Firefox.
|
||||
ContextMenus,
|
||||
Menus,
|
||||
NetworkStatus,
|
||||
NormandyAddonStudy,
|
||||
Notifications,
|
||||
Omnibox,
|
||||
PageAction,
|
||||
Permissions,
|
||||
Pkcs11,
|
||||
Privacy,
|
||||
Proxy,
|
||||
Runtime,
|
||||
Scripting,
|
||||
Search,
|
||||
Sessions,
|
||||
SidebarAction,
|
||||
Storage,
|
||||
Tabs,
|
||||
Theme,
|
||||
TopSites,
|
||||
Types,
|
||||
UserScripts,
|
||||
WebNavigation,
|
||||
WebRequest,
|
||||
Windows,
|
||||
} from 'webextension-polyfill';
|
||||
@@ -1,28 +1,27 @@
|
||||
import { addViteConfig, defineWxtModule } from '../modules';
|
||||
import type {
|
||||
EslintGlobalsPropValue,
|
||||
Wxt,
|
||||
WxtDirFileEntry,
|
||||
WxtModule,
|
||||
WxtResolvedUnimportOptions,
|
||||
} from '../types';
|
||||
import { type Unimport, createUnimport } from 'unimport';
|
||||
import { type Unimport, createUnimport, toExports } from 'unimport';
|
||||
import UnimportPlugin from 'unimport/unplugin';
|
||||
|
||||
export default defineWxtModule({
|
||||
name: 'wxt:built-in:unimport',
|
||||
setup(wxt) {
|
||||
const options = wxt.config.imports;
|
||||
if (options === false) return;
|
||||
|
||||
let unimport: Unimport;
|
||||
const isEnabled = () => !wxt.config.imports.disabled;
|
||||
|
||||
// Add user module imports to config
|
||||
wxt.hooks.hook('config:resolved', () => {
|
||||
const addModuleImports = (module: WxtModule<any>) => {
|
||||
if (!module.imports) return;
|
||||
|
||||
options.imports ??= [];
|
||||
options.imports.push(...module.imports);
|
||||
wxt.config.imports.imports ??= [];
|
||||
wxt.config.imports.imports.push(...module.imports);
|
||||
};
|
||||
|
||||
wxt.config.builtinModules.forEach(addModuleImports);
|
||||
@@ -33,7 +32,7 @@ export default defineWxtModule({
|
||||
// config inside "config:resolved" are applied.
|
||||
wxt.hooks.afterEach((event) => {
|
||||
if (event.name === 'config:resolved') {
|
||||
unimport = createUnimport(options);
|
||||
unimport = createUnimport(wxt.config.imports);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -42,17 +41,29 @@ export default defineWxtModule({
|
||||
// Update cache before each rebuild
|
||||
await unimport.init();
|
||||
|
||||
// Always generate the #import module types
|
||||
entries.push(await getImportsModuleEntry(wxt, unimport));
|
||||
|
||||
if (!isEnabled()) return;
|
||||
|
||||
// Only create global types when user has enabled auto-imports
|
||||
entries.push(await getImportsDeclarationEntry(unimport));
|
||||
|
||||
if (options.eslintrc.enabled === false) return;
|
||||
if (wxt.config.imports.eslintrc.enabled === false) return;
|
||||
|
||||
// Only generate ESLint config if that feature is enabled
|
||||
entries.push(
|
||||
await getEslintConfigEntry(unimport, options.eslintrc.enabled, options),
|
||||
await getEslintConfigEntry(
|
||||
unimport,
|
||||
wxt.config.imports.eslintrc.enabled,
|
||||
wxt.config.imports,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
// Add vite plugin
|
||||
addViteConfig(wxt, () => ({
|
||||
plugins: [UnimportPlugin.vite(options)],
|
||||
plugins: [UnimportPlugin.vite(wxt.config.imports)],
|
||||
}));
|
||||
},
|
||||
});
|
||||
@@ -60,9 +71,6 @@ export default defineWxtModule({
|
||||
async function getImportsDeclarationEntry(
|
||||
unimport: Unimport,
|
||||
): Promise<WxtDirFileEntry> {
|
||||
// Load project imports into unimport memory so they are output via generateTypeDeclarations
|
||||
await unimport.init();
|
||||
|
||||
return {
|
||||
path: 'types/imports.d.ts',
|
||||
text: [
|
||||
@@ -74,6 +82,25 @@ async function getImportsDeclarationEntry(
|
||||
};
|
||||
}
|
||||
|
||||
async function getImportsModuleEntry(
|
||||
wxt: Wxt,
|
||||
unimport: Unimport,
|
||||
): Promise<WxtDirFileEntry> {
|
||||
const imports = await unimport.getImports();
|
||||
return {
|
||||
path: 'types/imports-module.d.ts',
|
||||
text: [
|
||||
'// Generated by wxt',
|
||||
'// Types for the #import virtual module',
|
||||
"declare module '#imports' {",
|
||||
` ${toExports(imports, wxt.config.wxtDir, true).replaceAll('\n', '\n ')}`,
|
||||
'}',
|
||||
'',
|
||||
].join('\n'),
|
||||
tsReference: true,
|
||||
};
|
||||
}
|
||||
|
||||
async function getEslintConfigEntry(
|
||||
unimport: Unimport,
|
||||
version: 8 | 9,
|
||||
@@ -114,6 +141,7 @@ export default {
|
||||
name: "wxt/auto-imports",
|
||||
languageOptions: {
|
||||
globals,
|
||||
/** @type {import('eslint').Linter.SourceType} */
|
||||
sourceType: "module",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -16,7 +16,8 @@ cli
|
||||
.option('-c, --config <file>', 'use specified config file')
|
||||
.option('-m, --mode <mode>', 'set env mode')
|
||||
.option('-b, --browser <browser>', 'specify a browser')
|
||||
.option('-p, --port <port>', 'specify a port for the dev server')
|
||||
.option('--host <host>', 'specify a host for the dev server to bind to')
|
||||
.option('-p, --port <port>', 'specify a port for the dev server to bind to')
|
||||
.option(
|
||||
'-e, --filter-entrypoint <entrypoint>',
|
||||
'only build specific entrypoints',
|
||||
@@ -28,6 +29,12 @@ cli
|
||||
.option('--mv2', 'target manifest v2')
|
||||
.action(
|
||||
wrapAction(async (root, flags) => {
|
||||
const serverOptions: NonNullable<
|
||||
NonNullable<Parameters<typeof createServer>[0]>['dev']
|
||||
>['server'] = {};
|
||||
if (flags.host) serverOptions.host = flags.host;
|
||||
if (flags.port) serverOptions.port = parseInt(flags.port);
|
||||
|
||||
const server = await createServer({
|
||||
root,
|
||||
mode: flags.mode,
|
||||
@@ -37,13 +44,9 @@ cli
|
||||
debug: flags.debug,
|
||||
filterEntrypoints: getArrayFromFlags(flags, 'filterEntrypoint'),
|
||||
dev:
|
||||
flags.port == null
|
||||
Object.keys(serverOptions).length === 0
|
||||
? undefined
|
||||
: {
|
||||
server: {
|
||||
port: parseInt(flags.port),
|
||||
},
|
||||
},
|
||||
: { server: serverOptions },
|
||||
});
|
||||
await server.start();
|
||||
return { isOngoing: true };
|
||||
|
||||
@@ -2,9 +2,6 @@ import cli from './commands';
|
||||
import { version } from '../version';
|
||||
import { isAliasedCommand } from './cli-utils';
|
||||
|
||||
// TODO: Remove. See https://github.com/wxt-dev/wxt/issues/277
|
||||
process.env.VITE_CJS_IGNORE_WARNING = 'true';
|
||||
|
||||
// Grab the command that we're trying to run
|
||||
cli.parse(process.argv, { run: false });
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './content-script-context';
|
||||
export * from './ui';
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Any runtime APIs that use the web extension APIs.
|
||||
*
|
||||
* @module wxt/client
|
||||
*/
|
||||
export * from './content-scripts';
|
||||
export * from './app-config';
|
||||
export * from './inject-script';
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
import { Hookable } from 'hookable';
|
||||
import { toArray } from '../../utils/arrays';
|
||||
import { safeVarName } from '../../utils/strings';
|
||||
import { importEntrypointFile } from '../../utils/building';
|
||||
import { ViteNodeServer } from 'vite-node/server';
|
||||
import { ViteNodeRunner } from 'vite-node/client';
|
||||
import { installSourcemapsSupport } from 'vite-node/source-map';
|
||||
@@ -83,7 +82,6 @@ export async function createViteBuilder(
|
||||
wxtPlugins.tsconfigPaths(wxtConfig),
|
||||
wxtPlugins.noopBackground(),
|
||||
wxtPlugins.globals(wxtConfig),
|
||||
wxtPlugins.resolveExtensionApi(wxtConfig),
|
||||
wxtPlugins.defineImportMeta(),
|
||||
wxtPlugins.wxtPluginLoader(wxtConfig),
|
||||
wxtPlugins.resolveAppConfig(wxtConfig),
|
||||
@@ -286,44 +284,26 @@ export async function createViteBuilder(
|
||||
version: vite.version,
|
||||
async importEntrypoint(path) {
|
||||
const env = createExtensionEnvironment();
|
||||
switch (wxtConfig.entrypointLoader) {
|
||||
default:
|
||||
case 'jiti': {
|
||||
return await env.run(() => importEntrypointFile(path));
|
||||
}
|
||||
case 'vite-node': {
|
||||
const { runner, server } = await createViteNodeImporter([path]);
|
||||
const res = await env.run(() => runner.executeFile(path));
|
||||
await server.close();
|
||||
requireDefaultExport(path, res);
|
||||
return res.default;
|
||||
}
|
||||
}
|
||||
const { runner, server } = await createViteNodeImporter([path]);
|
||||
const res = await env.run(() => runner.executeFile(path));
|
||||
await server.close();
|
||||
requireDefaultExport(path, res);
|
||||
return res.default;
|
||||
},
|
||||
async importEntrypoints(paths) {
|
||||
const env = createExtensionEnvironment();
|
||||
switch (wxtConfig.entrypointLoader) {
|
||||
default:
|
||||
case 'jiti': {
|
||||
return await env.run(() =>
|
||||
Promise.all(paths.map(importEntrypointFile)),
|
||||
);
|
||||
}
|
||||
case 'vite-node': {
|
||||
const { runner, server } = await createViteNodeImporter(paths);
|
||||
const res = await env.run(() =>
|
||||
Promise.all(
|
||||
paths.map(async (path) => {
|
||||
const mod = await runner.executeFile(path);
|
||||
requireDefaultExport(path, mod);
|
||||
return mod.default;
|
||||
}),
|
||||
),
|
||||
);
|
||||
await server.close();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
const { runner, server } = await createViteNodeImporter(paths);
|
||||
const res = await env.run(() =>
|
||||
Promise.all(
|
||||
paths.map(async (path) => {
|
||||
const mod = await runner.executeFile(path);
|
||||
requireDefaultExport(path, mod);
|
||||
return mod.default;
|
||||
}),
|
||||
),
|
||||
);
|
||||
await server.close();
|
||||
return res;
|
||||
},
|
||||
async build(group) {
|
||||
let entryConfig;
|
||||
@@ -347,9 +327,9 @@ export async function createViteBuilder(
|
||||
async createServer(info) {
|
||||
const serverConfig: vite.InlineConfig = {
|
||||
server: {
|
||||
host: info.host,
|
||||
port: info.port,
|
||||
strictPort: true,
|
||||
host: info.hostname,
|
||||
origin: info.origin,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('Dev HTML Prerender Plugin', () => {
|
||||
['~local/style.css', 'http://localhost:5173/style.css'],
|
||||
['~absolute/style.css', 'http://localhost:5173/assets/style.css'],
|
||||
['~file', 'http://localhost:5173/example.css'],
|
||||
// Absolute paths are loaded with the `/@fs/` base path
|
||||
// Paths outside the project root are loaded with the `/@fs/` base path
|
||||
[
|
||||
'~outside/test.css',
|
||||
`http://localhost:5173/@fs${
|
||||
@@ -48,7 +48,7 @@ describe('Dev HTML Prerender Plugin', () => {
|
||||
},
|
||||
});
|
||||
const server = fakeDevServer({
|
||||
hostname: 'localhost',
|
||||
host: 'localhost',
|
||||
port: 5173,
|
||||
origin: 'http://localhost:5173',
|
||||
});
|
||||
|
||||
@@ -4,10 +4,10 @@ import { getEntrypointName } from '../../../utils/entrypoints';
|
||||
import { parseHTML } from 'linkedom';
|
||||
import { dirname, relative, resolve } from 'node:path';
|
||||
import { normalizePath } from '../../../utils/paths';
|
||||
import { murmurHash } from 'ohash';
|
||||
import { hash } from 'ohash';
|
||||
|
||||
// Stored outside the plugin to effect all instances of the devHtmlPrerender plugin.
|
||||
const inlineScriptContents: Record<number, string> = {};
|
||||
const inlineScriptContents: Record<string, string> = {};
|
||||
|
||||
/**
|
||||
* Pre-renders the HTML entrypoints when building the extension to connect to the dev server.
|
||||
@@ -85,13 +85,13 @@ export function devHtmlPrerender(
|
||||
inlineScripts.forEach((script) => {
|
||||
// Save the text content for later
|
||||
const textContent = script.textContent ?? '';
|
||||
const hash = murmurHash(textContent);
|
||||
inlineScriptContents[hash] = textContent;
|
||||
const textHash = hash(textContent);
|
||||
inlineScriptContents[textHash] = textContent;
|
||||
|
||||
// Replace unsafe inline script
|
||||
const virtualScript = document.createElement('script');
|
||||
virtualScript.type = 'module';
|
||||
virtualScript.src = `${server.origin}/@id/${virtualInlineScript}?${hash}`;
|
||||
virtualScript.src = `${server.origin}/@id/${virtualInlineScript}?${textHash}`;
|
||||
script.replaceWith(virtualScript);
|
||||
});
|
||||
|
||||
@@ -128,8 +128,8 @@ export function devHtmlPrerender(
|
||||
// Resolve virtualized inline scripts
|
||||
if (id.startsWith(resolvedVirtualInlineScript)) {
|
||||
// id="virtual:wxt-inline-script?<hash>"
|
||||
const hash = Number(id.substring(id.indexOf('?') + 1));
|
||||
return inlineScriptContents[hash];
|
||||
const newHash = hash(id.substring(id.indexOf('?')));
|
||||
return inlineScriptContents[newHash];
|
||||
}
|
||||
|
||||
// Ignore chunks during HTML file pre-rendering
|
||||
|
||||
@@ -15,9 +15,9 @@ export function devServerGlobals(
|
||||
|
||||
return {
|
||||
define: {
|
||||
__DEV_SERVER_PROTOCOL__: JSON.stringify('ws:'),
|
||||
__DEV_SERVER_HOSTNAME__: JSON.stringify(server.hostname),
|
||||
__DEV_SERVER_PORT__: JSON.stringify(server.port),
|
||||
__DEV_SERVER_ORIGIN__: JSON.stringify(
|
||||
server.origin.replace(/^http(s):/, 'ws$1:'),
|
||||
),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -3,12 +3,7 @@ import type * as vite from 'vite';
|
||||
import { ResolvedConfig } from '../../../../types';
|
||||
|
||||
/**
|
||||
* Mock `webextension-polyfill`, `wxt/browser`, and `wxt/browser/*` by inlining
|
||||
* all dependencies that import them and adding a custom alias so that Vite
|
||||
* resolves to a mocked version of the module.
|
||||
*
|
||||
* TODO: Detect non-wxt dependencies (like `@webext-core/*`) that import `webextension-polyfill` via
|
||||
* `npm list` and inline them automatically.
|
||||
* Mock `wxt/browser` and stub the global `browser`/`chrome` types with a fake version of the extension APIs
|
||||
*/
|
||||
export function extensionApiMock(config: ResolvedConfig): vite.PluginOption {
|
||||
const virtualSetupModule = 'virtual:wxt-setup';
|
||||
@@ -27,14 +22,12 @@ export function extensionApiMock(config: ResolvedConfig): vite.PluginOption {
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{ find: 'webextension-polyfill', replacement },
|
||||
// wxt/browser, wxt/browser/...
|
||||
{ find: /^wxt\/browser.*/, replacement },
|
||||
{ find: 'wxt/browser', replacement },
|
||||
],
|
||||
},
|
||||
ssr: {
|
||||
// Inline all WXT modules so vite processes them so the aliases can
|
||||
// be resolved
|
||||
// Inline all WXT modules subdependencies can be mocked
|
||||
noExternal: ['wxt'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ export * from './cssEntrypoints';
|
||||
export * from './bundleAnalysis';
|
||||
export * from './globals';
|
||||
export * from './extensionApiMock';
|
||||
export * from './resolveExtensionApi';
|
||||
export * from './entrypointGroupGlobals';
|
||||
export * from './defineImportMeta';
|
||||
export * from './removeEntrypointMainFunction';
|
||||
|
||||
@@ -15,7 +15,7 @@ export function noopBackground(): Plugin {
|
||||
},
|
||||
load(id) {
|
||||
if (id === resolvedVirtualModuleId) {
|
||||
return `import { defineBackground } from 'wxt/sandbox';\nexport default defineBackground(() => void 0)`;
|
||||
return `import { defineBackground } from 'wxt/utils/define-background';\nexport default defineBackground(() => void 0)`;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { ResolvedConfig } from '../../../../types';
|
||||
import type * as vite from 'vite';
|
||||
|
||||
/**
|
||||
* Apply the experimental config for which extension API is used. This only
|
||||
* effects the extension API included at RUNTIME - during development, types
|
||||
* depend on the import.
|
||||
*
|
||||
* NOTE: this only works if we import `wxt/browser` instead of using the relative path.
|
||||
*/
|
||||
export function resolveExtensionApi(config: ResolvedConfig): vite.Plugin {
|
||||
return {
|
||||
name: 'wxt:resolve-extension-api',
|
||||
config() {
|
||||
// Only apply the config if we're disabling the polyfill
|
||||
if (config.extensionApi === 'webextension-polyfill') return;
|
||||
|
||||
return {
|
||||
resolve: {
|
||||
alias: [
|
||||
{ find: /^wxt\/browser$/, replacement: 'wxt/browser/chrome' },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -48,7 +48,7 @@ try {
|
||||
handler(html, _ctx) {
|
||||
const src =
|
||||
config.command === 'serve'
|
||||
? `http://${config.dev.server?.hostname}:${config.dev.server?.port}/@id/${virtualHtmlModuleId}`
|
||||
? `${config.dev.server?.origin}/@id/${virtualHtmlModuleId}`
|
||||
: virtualHtmlModuleId;
|
||||
|
||||
const { document } = parseHTML(html);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user