Compare commits

...

39 Commits

Author SHA1 Message Date
GitHub Actions e632a2964a chore(release): @wxt-dev/module-react v1.1.1 2024-08-06 01:55:29 +00:00
GitHub Actions f736a14651 chore(release): @wxt-dev/auto-icons v1.0.2 2024-08-05 23:52:51 +00:00
uncenter a8b0d40959 docs(auto-icons): fix configuration example typo (#905) 2024-08-05 18:48:54 -05:00
Aaron 4c5504d139 Update changelog 2024-08-04 10:18:02 -05:00
GitHub Actions fb86deb388 chore(release): wxt v0.19.4 2024-08-04 14:31:30 +00:00
Aaron 3a8e6135ac fix: Fix createIframeUi page option types 2024-08-04 09:27:46 -05:00
Aaron a38de0c03f feat: Add injectScript helper (#900) 2024-08-04 09:16:46 -05:00
Aaron ea97410fb4 fix(types): PublicPath type resolution with extensionApi: "chrome" (#901) 2024-08-04 08:59:45 -05:00
hikiko4ern e36549ddce fix: do not clear .wxt/tsconfig.json in findEntrypoints if it exists (#898) 2024-08-03 21:59:12 -05:00
KnightYoshi 10851ae01c docs: add missing extension-apis segment (#896) 2024-08-03 19:28:53 -05:00
GitHub Actions d553ef6b29 chore(release): wxt v0.19.3 2024-08-02 21:48:05 +00:00
Aaron 15bf0da82e fix(content-script-context): Fix initialization logic for Firefox (#895) 2024-08-02 16:43:40 -05:00
hikiko4ern fc246ffaac fix: add consola to wxt dependencies (#892) 2024-08-01 19:05:28 -05:00
Florian Metz 9785eff21e chore: add more metadata for npm (#885) 2024-08-01 18:00:00 -05:00
Himanshu Patil 52fbd2c99e docs: Improve prepare:types hook JSDoc (#886) 2024-08-01 17:11:20 -05:00
GitHub Actions 648ae4fb8d chore(release): @wxt-dev/auto-icons v1.0.1 2024-07-30 18:23:12 +00:00
Aaron Klinker bbeeabc9ea Revert version change for auto-icons module 2024-07-30 13:18:35 -05:00
web-dahuyou 35cf6e7bc0 docs: Add "NiceTab" to homepage (#882) 2024-07-30 13:12:00 -05:00
Florian Metz 78822acdac fix(auto-icons): path option (#880)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-30 13:06:41 -05:00
不游 aea123890f docs: Add "1Proompt" to homepage (#879) 2024-07-29 07:50:28 -05:00
Matt Strayer c369f4a955 docs: Add "mindful" to homepage (#878) 2024-07-28 16:33:58 -05:00
GitHub Actions 16d8240406 chore(release): wxt v0.19.2 2024-07-28 15:49:52 +00:00
Aaron 24cd01dd31 fix: Don't default to dev mode for production builds when using vite-node loader (#877) 2024-07-28 10:45:32 -05:00
Aaron fb22beadb8 fix: Remove unused top-level functions and variables when loading entrypoints with the vite-node loader (#875) 2024-07-28 09:27:29 -05:00
Aaron Klinker fa85177648 docs(modules): Adjust wording 2024-07-27 17:04:36 -05:00
Aaron Klinker ed07a49dd2 docs: Update README and homepage features with WXT modules 2024-07-27 16:57:33 -05:00
Aaron 09c9de0a60 docs: Fix environment variable reference (#870) 2024-07-27 16:15:28 -05:00
Aaron 523296b29a chore(deps): Upgrade all dependencies (#869) 2024-07-27 16:07:50 -05:00
Aaron Klinker ef67ef47aa chore: Update template package names 2024-07-27 15:37:48 -05:00
Aaron Klinker cb5eb68d62 Fix changelog 2024-07-27 15:11:27 -05:00
GitHub Actions 2e9ab75e2b chore(release): wxt v0.19.1 2024-07-27 20:09:05 +00:00
Aaron 0c2d795800 pref: Tree-shake unrelated code inside vite-node entrypoint loader (#867) 2024-07-27 15:03:23 -05:00
Aaron 97aa173f7e fix: Define web globals when importing entrypoints (#865) 2024-07-27 14:48:55 -05:00
Aaron 9eb64d94d6 docs: Add README for auto-icons module 2024-07-26 14:00:37 -05:00
Florian Metz a0971cab52 feat: Auto Icons Module (#851)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-25 23:41:53 -05:00
Aaron 127562e9d0 ci: Fix breaking changes link generation in changelog 2024-07-25 22:48:05 -05:00
Aaron 038e7450e0 Fix diff 2024-07-25 22:40:33 -05:00
Aaron 6c00763187 docs: Add missing ssr.noExternal docs to vite-node loader 2024-07-25 22:38:21 -05:00
Aaron 2da0f3d686 Fix changelog 2024-07-25 22:14:06 -05:00
72 changed files with 6559 additions and 4306 deletions
+1
View File
@@ -12,6 +12,7 @@ on:
- module-vue
- module-svelte
- module-solid
- auto-icons
jobs:
validate:
+1 -1
View File
@@ -59,10 +59,10 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
- 🦾 Auto-imports
- 🤖 Automated publishing
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
- 📦 Modular architecture with [WXT modules](https://wxt.dev/guide/go-further/reusable-modules.html#overview)
- 🖍️ Quickly bootstrap a new project
- 📏 Bundle analysis
- ⬇️ Download and bundle remote URL imports
- 📦 Modular architecture with [modules](https://wxt.dev/guide/go-further/reusable-modules.html#overview)
## Contributors
@@ -35,6 +35,9 @@ const chromeExtensionIds = [
'bcpgdpedphodjcjlminjbdeejccjbimp', // 汇率转换-中文版本
'loeilaonggnalkaiiaepbegccilkmjjp', // Currency Converter Plus
'npcnninnjghigjfiecefheeibomjpkak', // Respond Easy
'cfkdcideecefncbglkhneoflfnmhoicc', // mindful - stay focused on your goals
'lnhejcpclabmbgpiiomjbhalblnnbffg', // 1Proompt
'fonflmjnjbkigocpoommgmhljdpljain', // NiceTab - https://github.com/web-dahuyou/NiceTab
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
+1 -1
View File
@@ -142,7 +142,7 @@ export default defineConfig({
menuItem('Custom Events', 'custom-events'),
menuItem('Reusable Modules', 'reusable-modules'),
menuItem('Remote Code', 'remote-code'),
menuItem('Entrypoint Side Effects', 'entrypoint-side-effects'),
menuItem('Entrypoint Loaders', 'entrypoint-loaders'),
menuItem('How WXT Works', 'how-wxt-works'),
]),
menuGroup('Upgrade Guide', '/guide/upgrade-guide/', [
+3 -3
View File
@@ -53,9 +53,9 @@ declare module 'wxt/sandbox' {
}
export default defineAppConfig({
bugReportingDisabled: process.env.VITE_BUG_REPORTING_DISABLED === 'true',
apiKey: process.env.VITE_API_KEY,
bugReportingDisabled: import.meta.env.VITE_BUG_REPORTING_DISABLED === 'true',
apiKey: import.meta.env.VITE_API_KEY,
});
```
> You don't have to do this, you can use `process.env.VITE_*` anywhere in your runtime code, but putting them here consolidates them to one place and defines what variables are expected.
> You don't have to do this, you can use `import.meta.env.VITE_*` anywhere in your runtime code, but putting them here consolidates them to one place and defines what variables are expected.
+45 -2
View File
@@ -1,6 +1,6 @@
# Entrypoint Loaders
Because entrypoint options, like content script matches, are listed in the entrypoint's JS file, WXT has to import them during the build process to use those options when generating the manifest.
Because entrypoint options, like content script `matches`, are listed in the entrypoint's JS file, WXT has to import them during the build process to use those options when generating the manifest.
There are two options for loading your entrypoints:
@@ -11,7 +11,50 @@ There are two options for loading your entrypoints:
Since 0.19.0, WXT uses `vite-node`, the same tool that powers Vitest and Nuxt, to import your entrypoint files.
There isn't really anything to add here... By default, it should "just work".
If you use any runtime packages that depend on `webextension-polyfill`, you need to add them to [Vite's `ssr.noExternal` option](https://vitejs.dev/config/ssr-options#ssr-noexternal):
```ts
export default defineConfig({
vite: () => ({
ssr: {
noExternal: ['@webext-core/messaging', '@webext-core/proxy-service'],
},
}),
});
```
:::details Why?
This tells Vite it needs process these module's, letting WXT properly disable the polyfill in the NodeJS environment so it doesn't cause any build errors like this:
```
ERROR This script should only be loaded in a browser extension
```
:::
To get a list of installed packages that use on `webextension-polyfill`, run your package manager's `list` command. Here's an example with PNPM:
```sh
$ pnpm why webextension-polyfill
dependencies:
@webext-core/messaging 1.4.0
└── webextension-polyfill 0.10.0
@webext-core/proxy-service 1.2.0
├─┬ @webext-core/messaging 1.4.0 peer
│ └── webextension-polyfill 0.10.0
└── webextension-polyfill 0.12.0 peer
devDependencies:
@wxt-dev/module-vue 1.0.0
└─┬ wxt 0.19.0-alpha1 peer
└── webextension-polyfill 0.12.0
webextension-polyfill 0.12.0
wxt 0.19.0-alpha1
└── webextension-polyfill 0.12.0
```
Ignoring WXT itself (it's added automatically for you), there are three packages that depend on the polyfill: `@wxt-dev/module-vue`, `@webext-core/messaging`, and `@webext-core/proxy-service`. Since the vue module is a build dependency, with no runtime code, you don't have to add it. That means for this case, you need to add `@webext-core/messaging`, and `@webext-core/proxy-service`, as shown in the original code snippet.
## jiti
+4 -4
View File
@@ -25,12 +25,12 @@ There are two ways to add a module to your project:
});
```
2. **NPM package**: By convention, any package on NPM that starts with `@wxt-dev/module-*` or `wxt-module-*` can be installed and included in your project:
2. **NPM package**: Find WXT modules on NPM and include them in your project:
```ts
// wxt.config.ts
export default defineConfig({
// Add the module to your project
modules: ['wxt-module-example'],
modules: ['@wxt-dev/auto-icons'],
});
```
@@ -99,9 +99,9 @@ export default defineConfig({
### Actually Doing Something
The first argument of the setup function, `wxt`, provides full access to the current build's context. You can access the resolved configuration via `wxt.config`, or setup hooks to manipulate the build at different steps of the build process.
The first argument of the setup function, `wxt`, provides full access to the current build's context. You can access the resolved configuration via `wxt.config`, or setup hooks to manipulate the build at different steps of the build process with `wxt.hooks`.
Here's an example that shows you how to update the `outDir` based on the build mode. It does a good job showing how to access config, set up a hook, and do something when the hook is called.
Here's an example that updates the `outDir` based on the build mode. It's a very simple example of how to access config and setup a hook.
```ts
export default defineWxtModule((wxt) => {
+44 -25
View File
@@ -14,37 +14,56 @@ If there was a major version change, follow the steps below to fix breaking chan
### `vite-node` Entrypoint Loader
The default entrypoint loader has changed to `vite-node`. This change enables:
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:
- Importing variables and using them in the entrypoint options.
<!-- prettier-ignore -->
```ts
export default defineConfig({
vite: () => ({ // [!code ++]
ssr: { // [!code ++]
noExternal: ['@webext-core/messaging', '@webext-core/proxy-service'], // [!code ++]
}, // [!code ++]
}), // [!code ++]
});
```
```ts
// entrypoints/content.ts
import { GOOGLE_MATCHES } from '~/utils/constants'
> [Read the full docs](/guide/go-further/entrypoint-loaders#vite-node) for more information.
export default defineContentScript({
matches: [GOOGLE_MATCHES],
main: () => ...,
})
```
:::details This change enables:
- Use Vite-specific APIs like `import.meta.glob` to define entrypoint options.
Importing variables and using them in the entrypoint options:
```ts
// entrypoints/content.ts
const providers: Record<string, any> = import.meta.glob('../providers/*', {
eager: true,
});
```ts
// entrypoints/content.ts
import { GOOGLE_MATCHES } from '~/utils/constants'
export default defineContentScript({
matches: Object.values(providers).flatMap(
(provider) => provider.default.paths,
),
async main() {
console.log('Hello content.');
},
});
```
export default defineContentScript({
matches: [GOOGLE_MATCHES],
main: () => ...,
})
```
Using Vite-specific APIs like `import.meta.glob` to define entrypoint options:
```ts
// entrypoints/content.ts
const providers: Record<string, any> = import.meta.glob('../providers/*', {
eager: true,
});
export default defineContentScript({
matches: Object.values(providers).flatMap(
(provider) => provider.default.paths,
),
async main() {
console.log('Hello content.');
},
});
```
Basically, you can now import and do things outside the `main` function of the entrypoint - you could not do that before. Still though, be careful. It is recommended to avoid running code outside the `main` function to keep your builds fast.
:::
To continue using the old approach, add the following to your `wxt.config.ts` file:
+5
View File
@@ -53,6 +53,11 @@ features:
details: Works with any front-end framework with a Vite plugin.
link: /guide/key-concepts/frontend-frameworks
linkText: Add a framework
- icon: 📦
title: Modular Architecture
details: Reuse build-time and runtime-code across multiple extensions.
link: /guide/go-further/reusable-modules
linkText: Read docs
- icon: 🖍️
title: Bootstrap a New Project
details: Get started quickly with several awesome project templates.
+7 -8
View File
@@ -2,10 +2,9 @@
"private": true,
"type": "module",
"engines": {
"node": ">=18.20.3",
"pnpm": "^8.6.3"
"node": ">=18.20.3"
},
"packageManager": "pnpm@8.15.9",
"packageManager": "pnpm@9.6.0",
"scripts": {
"check": "check && pnpm -r --sequential run check",
"test": "pnpm -r --sequential run test run",
@@ -21,7 +20,7 @@
"@aklinker1/buildc": "^1.0.11",
"@aklinker1/check": "^1.3.1",
"@types/fs-extra": "^11.0.4",
"@vitest/coverage-v8": "^2.0.0",
"@vitest/coverage-v8": "^2.0.4",
"changelogen": "^0.5.5",
"consola": "^3.2.3",
"dependency-graph": "^1.0.0",
@@ -37,12 +36,12 @@
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "4.0.0-next.23",
"typedoc-vitepress-theme": "1.0.0-next.3",
"typescript": "^5.5.3",
"typescript": "^5.5.4",
"vitepress": "^1.3.1",
"vitest-mock-extended": "^1.3.2",
"vue": "^3.4.33",
"vitest-mock-extended": "^2.0.0",
"vue": "^3.4.34",
"wxt": "workspace:*",
"yaml": "^2.4.5"
"yaml": "^2.5.0"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
+34
View File
@@ -0,0 +1,34 @@
# Changelog
## v1.0.2
[compare changes](https://github.com/wxt-dev/wxt/compare/auto-icons-v1.0.1...auto-icons-v1.0.2)
### 📖 Documentation
- **auto-icons:** Fix configuration example typo ([#905](https://github.com/wxt-dev/wxt/pull/905))
### 🏡 Chore
- Add more metadata for npm ([#885](https://github.com/wxt-dev/wxt/pull/885))
### ❤️ Contributors
- Uncenter ([@uncenter](http://github.com/uncenter))
- Florian Metz ([@Timeraa](http://github.com/Timeraa))
## v1.0.1
[compare changes](https://github.com/wxt-dev/wxt/compare/auto-icons-v1.0.0...auto-icons-v1.0.1)
### 🩹 Fixes
- **auto-icons:** Path option ([#880](https://github.com/wxt-dev/wxt/pull/880))
### 🏡 Chore
- **deps:** Upgrade all dependencies ([#869](https://github.com/wxt-dev/wxt/pull/869))
### ❤️ Contributors
- Florian Metz ([@Timeraa](http://github.com/Timeraa))
+42
View File
@@ -0,0 +1,42 @@
# WXT Auto Icons
## Features
- Generate extension icons with the correct sizes
- Make the icon greyscale during development
## Usage
Install the package:
```sh
npm i --save-dev @wxt-dev/auto-icons
pnpm i -D @wxt-dev/auto-icons
yarn add --dev @wxt-dev/auto-icons
bun i -D @wxt-dev/auto-icons
```
Add the module to `wxt.config.ts`:
```ts
export default defineConfig({
modules: ['@wxt-dev/auto-icons'],
});
```
And finally, save the base icon to `<srcDir>/assets/icon.png`.
## Configuration
The module can be configured via the `autoIcons` config:
```ts
export default defineConfig({
modules: ['@wxt-dev/auto-icons'],
autoIcons: {
// ...
},
});
```
Options have JSDocs available in your editor, or you can read them in the source code: [`AutoIconsOptions`](./src/index.ts).
+59
View File
@@ -0,0 +1,59 @@
{
"name": "@wxt-dev/auto-icons",
"description": "WXT module for automatically generating extension icons in different sizes",
"repository": {
"type": "git",
"url": "git+https://github.com/wxt-dev/wxt.git",
"directory": "packages/auto-icons"
},
"homepage": "https://github.com/wxt-dev/wxt/blob/main/packages/auto-icons/README.md",
"keywords": [
"wxt",
"module",
"icons",
"manifest"
],
"author": {
"name": "Florian Metz",
"email": "me@timeraa.dev"
},
"contributors": [
{
"name": "Aaron Klinker",
"email": "aaronklinker1+wxt@gmail.com"
}
],
"license": "MIT",
"version": "1.0.2",
"type": "module",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
}
},
"files": [
"dist"
],
"scripts": {
"build": "buildc -- unbuild",
"check": "pnpm build && check"
},
"peerDependencies": {
"wxt": ">=0.19.0"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.9",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
},
"dependencies": {
"defu": "^6.1.4",
"fs-extra": "^11.2.0",
"sharp": "^0.33.4"
}
}
+108
View File
@@ -0,0 +1,108 @@
import 'wxt';
import { defineWxtModule } from 'wxt/modules';
import { resolve, relative } from 'node:path';
import defu from 'defu';
import sharp from 'sharp';
import { ensureDir, exists } from 'fs-extra';
export default defineWxtModule<AutoIconsOptions>({
name: '@wxt-dev/auto-icons',
configKey: 'autoIcons',
async setup(wxt, options) {
const parsedOptions = defu<Required<AutoIconsOptions>, AutoIconsOptions[]>(
options,
{
enabled: true,
baseIconPath: resolve(wxt.config.srcDir, 'assets/icon.png'),
grayscaleOnDevelopment: true,
sizes: [128, 48, 32, 16],
},
);
const resolvedPath = resolve(wxt.config.srcDir, parsedOptions.baseIconPath);
if (!parsedOptions.enabled)
return wxt.logger.warn(`\`[auto-icons]\` ${this.name} disabled`);
if (!(await exists(resolvedPath))) {
return wxt.logger.warn(
`\`[auto-icons]\` Skipping icon generation, no base icon found at ${relative(process.cwd(), resolvedPath)}`,
);
}
wxt.hooks.hook('build:manifestGenerated', async (wxt, manifest) => {
if (manifest.icons)
return wxt.logger.warn(
'`[auto-icons]` icons property found in manifest, overwriting with auto-generated icons',
);
manifest.icons = Object.fromEntries(
parsedOptions.sizes.map((size) => [size, `icons/${size}.png`]),
);
});
wxt.hooks.hook('build:done', async (wxt, output) => {
const image = sharp(resolvedPath).png();
if (
wxt.config.mode === 'development' &&
parsedOptions.grayscaleOnDevelopment
) {
image.grayscale();
}
const outputFolder = wxt.config.outDir;
for (const size of parsedOptions.sizes) {
const resized = image.resize(size);
ensureDir(resolve(outputFolder, 'icons'));
await resized.toFile(resolve(outputFolder, `icons/${size}.png`));
output.publicAssets.push({
type: 'asset',
fileName: `icons/${size}.png`,
});
}
});
wxt.hooks.hook('prepare:publicPaths', (wxt, paths) => {
for (const size of parsedOptions.sizes) {
paths.push(`icons/${size}.png`);
}
});
},
});
/**
* Options for the auto-icons module
*/
export interface AutoIconsOptions {
/**
* Enable auto-icons generation
* @default true
*/
enabled?: boolean;
/**
* Path to the image to use.
*
* Path is relative to the project's src directory.
* @default "<srcDir>/assets/icon.png"
*/
baseIconPath?: string;
/**
* Grayscale the image when in development mode to indicate development
* @default true
*/
grayscaleOnDevelopment?: boolean;
/**
* Sizes to generate icons for
* @default [128, 48, 32, 16]
*/
sizes?: number[];
}
declare module 'wxt' {
export interface InlineConfig {
autoIcons?: AutoIconsOptions;
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"exclude": ["node_modules/**", "dist/**"]
}
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## v1.1.1
[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.1.0...module-react-v1.1.1)
### 🏡 Chore
- **deps:** Bump all non-major dependencies ([#834](https://github.com/wxt-dev/wxt/pull/834))
- **deps:** Upgrade all dependencies ([#869](https://github.com/wxt-dev/wxt/pull/869))
- Add more metadata for npm ([#885](https://github.com/wxt-dev/wxt/pull/885))
### ❤️ Contributors
- Florian Metz ([@Timeraa](http://github.com/Timeraa))
## v1.1.0
[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.0.0...module-react-v1.1.0)
-1
View File
@@ -9,7 +9,6 @@ export default defineWxtModule<ReactModuleOptions>({
const { vite } = options ?? {};
addViteConfig(wxt, () => ({
// @ts-expect-error: Vite version conflicts
plugins: [react(vite)],
}));
+20 -3
View File
@@ -1,6 +1,23 @@
{
"name": "@wxt-dev/module-react",
"version": "1.1.0",
"description": "WXT module to enable React support",
"repository": {
"type": "git",
"url": "git+https://github.com/wxt-dev/wxt.git",
"directory": "packages/module-react"
},
"homepage": "https://github.com/wxt-dev/wxt/blob/main/packages/module-react/README.md",
"keywords": [
"wxt",
"module",
"react"
],
"author": {
"name": "Aaron Klinker",
"email": "aaronklinker1+wxt@gmail.com"
},
"license": "MIT",
"version": "1.1.1",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -22,7 +39,7 @@
],
"scripts": {
"dev": "wxt",
"check": "buildc --deps-only -- check",
"check": "pnpm build && check",
"build": "buildc -- unbuild",
"prepare": "buildc --deps-only -- wxt prepare"
},
@@ -39,7 +56,7 @@
"publint": "^0.2.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "^5.5.3",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
-1
View File
@@ -9,7 +9,6 @@ export default defineWxtModule<SolidModuleOptions>({
const { vite } = options ?? {};
addViteConfig(wxt, () => ({
// @ts-expect-error: Vite version mismatch
plugins: [solid(vite)],
build: {
target: 'esnext',
+20 -3
View File
@@ -1,5 +1,22 @@
{
"name": "@wxt-dev/module-solid",
"description": "WXT module to enable SolidJS support",
"repository": {
"type": "git",
"url": "git+https://github.com/wxt-dev/wxt.git",
"directory": "packages/module-solid"
},
"homepage": "https://github.com/wxt-dev/wxt/blob/main/packages/module-solid/README.md",
"keywords": [
"wxt",
"module",
"solidjs"
],
"author": {
"name": "Aaron Klinker",
"email": "aaronklinker1+wxt@gmail.com"
},
"license": "MIT",
"version": "1.1.1",
"type": "module",
"main": "./dist/index.cjs",
@@ -22,7 +39,7 @@
],
"scripts": {
"dev": "wxt",
"check": "buildc --deps-only -- check",
"check": "pnpm build && check",
"build": "buildc -- unbuild",
"prepare": "buildc --deps-only -- wxt prepare"
},
@@ -35,8 +52,8 @@
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.9",
"solid-js": "^1.8.18",
"typescript": "^5.5.3",
"solid-js": "^1.8.19",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
+19 -2
View File
@@ -1,5 +1,22 @@
{
"name": "@wxt-dev/module-svelte",
"description": "WXT module to enable Svelte support",
"repository": {
"type": "git",
"url": "git+https://github.com/wxt-dev/wxt.git",
"directory": "packages/module-svelte"
},
"homepage": "https://github.com/wxt-dev/wxt/blob/main/packages/module-svelte/README.md",
"keywords": [
"wxt",
"module",
"svelte"
],
"author": {
"name": "Aaron Klinker",
"email": "aaronklinker1+wxt@gmail.com"
},
"license": "MIT",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
@@ -22,7 +39,7 @@
],
"scripts": {
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
"check": "pnpm build && check"
},
"peerDependencies": {
"wxt": ">=0.18.6"
@@ -33,7 +50,7 @@
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.9",
"typescript": "^5.5.3",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
-1
View File
@@ -14,7 +14,6 @@ export default defineWxtModule<SvelteModuleOptions>({
addViteConfig(wxt, () => ({
plugins: [
// @ts-expect-error: Vite version mismatch
svelte({
// Using a svelte.config.js file causes a segmentation fault when importing the file
configFile: false,
+20 -3
View File
@@ -1,5 +1,22 @@
{
"name": "@wxt-dev/module-vue",
"description": "WXT module to enable Vue support",
"repository": {
"type": "git",
"url": "git+https://github.com/wxt-dev/wxt.git",
"directory": "packages/module-vue"
},
"homepage": "https://github.com/wxt-dev/wxt/blob/main/packages/module-vue/README.md",
"keywords": [
"wxt",
"module",
"vue"
],
"author": {
"name": "Aaron Klinker",
"email": "aaronklinker1+wxt@gmail.com"
},
"license": "MIT",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
@@ -22,18 +39,18 @@
],
"scripts": {
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
"check": "pnpm build && check"
},
"peerDependencies": {
"wxt": ">=0.18.6"
},
"dependencies": {
"@vitejs/plugin-vue": "^5.0.5"
"@vitejs/plugin-vue": "^5.1.1"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.9",
"typescript": "^5.5.3",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
+4 -3
View File
@@ -21,12 +21,13 @@
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/chrome": "^0.0.268",
"@types/chrome": "^0.0.269",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@wxt-dev/auto-icons": "workspace:*",
"sass": "^1.77.8",
"typescript": "^5.5.3",
"vitest": "^2.0.3",
"typescript": "^5.5.4",
"vitest": "^2.0.4",
"vitest-plugin-random-seed": "^1.1.0",
"wxt": "workspace:*"
},

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -20,7 +20,7 @@ export default defineBackground({
// @ts-expect-error: should only accept entrypoints or public assets
browser.runtime.getURL('/');
browser.runtime.getURL('/background.js');
browser.runtime.getURL('/icon/128.png');
browser.runtime.getURL('/icons/128.png');
browser.runtime.getURL('/example.html#hash');
browser.runtime.getURL('/example.html?query=param');
// @ts-expect-error: should only allow hashes/query params on HTML files
@@ -0,0 +1,3 @@
import autoIcons from '@wxt-dev/auto-icons';
export default autoIcons;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

+1 -4
View File
@@ -6,8 +6,5 @@ export default defineProject({
mockReset: true,
restoreMocks: true,
},
plugins: [
// @ts-expect-error: Vite version mismatch
WxtVitest(),
],
plugins: [WxtVitest()],
});
+75 -7
View File
@@ -1,8 +1,81 @@
# Changelog
## v0.19.4
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.3...wxt-v0.19.4)
### 🚀 Enhancements
- Add `injectScript` helper ([#900](https://github.com/wxt-dev/wxt/pull/900))
### 🩹 Fixes
- Do not clear `.wxt/tsconfig.json` in `findEntrypoints` if it exists ([#898](https://github.com/wxt-dev/wxt/pull/898))
- **types:** `PublicPath` type resolution with `extensionApi: "chrome"` ([#901](https://github.com/wxt-dev/wxt/pull/901))
- Fix `createIframeUi` `page` option types ([3a8e613](https://github.com/wxt-dev/wxt/commit/3a8e613))
### ❤️ Contributors
- Hikiko4ern ([@hikiko4ern](http://github.com/hikiko4ern))
## v0.19.3
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.2...wxt-v0.19.3)
### 🩹 Fixes
- Add `consola` to `wxt` dependencies ([#892](https://github.com/wxt-dev/wxt/pull/892))
- **content-script-context:** Fix initialization logic for Firefox ([#895](https://github.com/wxt-dev/wxt/pull/895))
### 📖 Documentation
- Improve `prepare:types` hook JSDoc ([#886](https://github.com/wxt-dev/wxt/pull/886))
### ❤️ Contributors
- Hikiko4ern ([@hikiko4ern](http://github.com/hikiko4ern))
- Himanshu Patil ([@mehimanshupatil](https://github.com/mehimanshupatil))
## v0.19.2
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.1...wxt-v0.19.2)
### 🩹 Fixes
- Remove unused top-level functions and variables when loading entrypoints with the `vite-node` loader ([#875](https://github.com/wxt-dev/wxt/pull/875))
- Don't default to dev mode for production builds when using `vite-node` loader ([#877](https://github.com/wxt-dev/wxt/pull/877))
### 📖 Documentation
- Update README and homepage features with WXT modules ([ed07a49](https://github.com/wxt-dev/wxt/commit/ed07a49))
### 🏡 Chore
- **deps:** Upgrade all dependencies ([#869](https://github.com/wxt-dev/wxt/pull/869))
## v0.19.1
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.0...wxt-v0.19.1)
### 🚀 Enhancements
- Auto Icons Module ([#851](https://github.com/wxt-dev/wxt/pull/851))
### 🔥 Performance
- Tree-shake unrelated code inside `vite-node` entrypoint loader ([#867](https://github.com/wxt-dev/wxt/pull/867))
### 🩹 Fixes
- Define web globals when importing entrypoints ([#865](https://github.com/wxt-dev/wxt/pull/865))
### ❤️ Contributors
- Florian Metz ([@Timeraa](http://github.com/Timeraa))
## v0.19.0
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.15...wxt-v0.19.0)
[⚠️ breaking changes](https://wxt.dev/guide/upgrade-guide/wxt) &bull; [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.15...wxt-v0.19.0)
### 🚀 Enhancements
@@ -36,11 +109,6 @@
- **deps:** Upgrade `minimatch` from 9 to 10 ([#840](https://github.com/wxt-dev/wxt/pull/840))
- **dev-deps:** Upgrade `happy-dom` from 13 to 14 ([#843](https://github.com/wxt-dev/wxt/pull/843))
#### ⚠️ Breaking Changes
- ⚠️ Use `vite-node` to load entrypoints by default ([#859](https://github.com/wxt-dev/wxt/pull/859))
- **size:** ⚠️ Switch from `tsup` to `unbuild` for building WXT ([#848](https://github.com/wxt-dev/wxt/pull/848))
### ❤️ Contributors
- Florian Metz ([@Timeraa](http://github.com/Timeraa))
@@ -2285,4 +2353,4 @@ Initial release of WXT. Full support for production builds and initial toolkit f
### 🤖 CI
- Create validation workflow ([#12](https://github.com/wxt-dev/wxt/pull/12))
- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13))
- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13))
+1
View File
@@ -59,6 +59,7 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
- 🦾 Auto-imports
- 🤖 Automated publishing
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
- 📦 Modular architecture with [WXT modules](https://wxt.dev/guide/go-further/reusable-modules.html#overview)
- 🖍️ Quickly bootstrap a new project
- 📏 Bundle analysis
- ⬇️ Download and bundle remote URL imports
@@ -19,6 +19,7 @@ exports[`Auto Imports > eslintrc > "enabled: 8" should output a JSON config file
"defineUnlistedScript": true,
"defineWxtPlugin": true,
"fakeBrowser": true,
"injectScript": true,
"storage": true,
"useAppConfig": true
}
@@ -44,6 +45,7 @@ const globals = {
"defineUnlistedScript": true,
"defineWxtPlugin": true,
"fakeBrowser": true,
"injectScript": true,
"storage": true,
"useAppConfig": true
}
@@ -77,6 +79,7 @@ exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config f
"defineUnlistedScript": true,
"defineWxtPlugin": true,
"fakeBrowser": true,
"injectScript": true,
"storage": true,
"useAppConfig": true
}
@@ -103,6 +106,7 @@ exports[`Auto Imports > eslintrc > should allow customizing the output 1`] = `
"defineUnlistedScript": "readonly",
"defineWxtPlugin": "readonly",
"fakeBrowser": "readonly",
"injectScript": "readonly",
"storage": "readonly",
"useAppConfig": "readonly"
}
@@ -31,6 +31,7 @@ describe('Auto Imports', () => {
const defineUnlistedScript: typeof import('wxt/sandbox')['defineUnlistedScript']
const defineWxtPlugin: typeof import('wxt/sandbox')['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']
}
@@ -280,7 +280,7 @@ describe('Output Directory Structure', () => {
var _a, _b;
import { l as logHello, i as initPlugins } from "./chunks/_virtual_wxt-plugins-OjKtWpmY.js";
function defineBackground(arg) {
if (typeof arg === "function") return { main: arg };
if (arg == null || typeof arg === "function") return { main: arg };
return arg;
}
const definition = defineBackground({
@@ -366,7 +366,7 @@ describe('Output Directory Structure', () => {
"use strict";
var _a, _b;
function defineBackground(arg) {
if (typeof arg === "function") return { main: arg };
if (arg == null || typeof arg === "function") return { main: arg };
return arg;
}
function logHello(name) {
+2 -2
View File
@@ -3,7 +3,7 @@ import { TestProject } from '../utils';
describe('Remote Code', () => {
it('should download "url:*" modules and include them in the final bundle', async () => {
const url = 'https://code.jquery.com/jquery-3.7.1.slim.min.js';
const url = 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js';
const project = new TestProject();
project.addFile(
'entrypoints/popup.ts',
@@ -16,7 +16,7 @@ describe('Remote Code', () => {
const output = await project.serializeFile('.output/chrome-mv3/popup.js');
expect(output).toContain(
// Some text that will hopefully be in future versions of this script
'jQuery v3.7.1',
'lodash.com',
);
expect(output).not.toContain(url);
expect(
+10 -9
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.19.0",
"version": "0.19.4",
"description": "Next gen framework for developing web extensions",
"repository": {
"type": "git",
@@ -72,7 +72,7 @@
"scripts": {
"wxt": "tsx src/cli/index.ts",
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- run-s -c check:*",
"check": "pnpm build && run-s -c check:*",
"check:default": "check",
"check:tsc-virtual": "tsc --noEmit -p src/virtual",
"test": "buildc --deps-only -- vitest",
@@ -91,6 +91,7 @@
"cac": "^6.7.14",
"chokidar": "^3.6.0",
"ci-info": "^4.0.0",
"consola": "^3.2.3",
"defu": "^6.1.4",
"dequal": "^2.0.3",
"esbuild": "^0.23.0",
@@ -116,20 +117,20 @@
"picocolors": "^1.0.1",
"prompts": "^2.4.2",
"publish-browser-extension": "^2.1.3",
"unimport": "^3.9.0",
"vite": "^5.3.4",
"vite-node": "^2.0.0",
"unimport": "^3.9.1",
"vite": "^5.3.5",
"vite-node": "^2.0.4",
"web-ext-run": "^0.2.1",
"webextension-polyfill": "^0.12.0"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"@faker-js/faker": "^8.4.1",
"@types/chrome": "^0.0.268",
"@types/chrome": "^0.0.269",
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
"@types/natural-compare": "^1.4.3",
"@types/node": "^20.14.11",
"@types/node": "^20.14.12",
"@types/normalize-path": "^3.0.2",
"@types/prompts": "^2.4.9",
"execa": "^9.3.0",
@@ -139,9 +140,9 @@
"p-map": "^7.0.2",
"publint": "^0.2.9",
"tsx": "4.15.7",
"typescript": "^5.5.3",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
"vitest": "^2.0.3",
"vitest": "^2.0.4",
"vitest-plugin-random-seed": "^1.1.0"
},
"peerDependencies": {
+2 -7
View File
@@ -1,4 +1,6 @@
/// <reference types="chrome" />
import type { WxtRuntime, WxtI18n } from './index';
/**
* EXPERIMENTAL
*
@@ -7,13 +9,6 @@
* @module wxt/browser/chrome
*/
export interface WxtRuntime {
// Overriden per-project
}
export interface WxtI18n {
// Overriden per-project
}
export type Chrome = typeof chrome;
export type WxtBrowser = Omit<Chrome, 'runtime' | 'i18n'> & {
runtime: WxtRuntime & Omit<Chrome['runtime'], 'getURL'>;
+7 -7
View File
@@ -4,18 +4,18 @@
* @module wxt/browser
*/
import originalBrowser, { Browser, Runtime, I18n } from 'webextension-polyfill';
import originalBrowser, { Browser } from 'webextension-polyfill';
export interface AugmentedBrowser extends Browser {
runtime: WxtRuntime;
i18n: WxtI18n;
}
export type AugmentedBrowser = Omit<Browser, 'runtime' | 'i18n'> & {
runtime: WxtRuntime & Omit<Browser['runtime'], 'getURL'>;
i18n: WxtI18n & Omit<Browser['i18n'], 'getMessage'>;
};
export interface WxtRuntime extends Runtime.Static {
export interface WxtRuntime {
// Overriden per-project
}
export interface WxtI18n extends I18n.Static {
export interface WxtI18n {
// Overriden per-project
}
@@ -0,0 +1,59 @@
/** @vitest-environment happy-dom */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ContentScriptContext } from '..';
import { fakeBrowser } from '@webext-core/fake-browser';
import { sleep } from '../../../core/utils/time';
describe('Content Script Context', () => {
beforeEach(() => {
vi.useRealTimers();
fakeBrowser.runtime.id = 'anything';
});
it("should recognize when the content script has lost it's connection to the extension API", () => {
const ctx = new ContentScriptContext('test');
const onInvalidated = vi.fn();
ctx.onInvalidated(onInvalidated);
// @ts-expect-error
delete fakeBrowser.runtime.id;
const isValid = ctx.isValid;
expect(onInvalidated).toBeCalled();
expect(isValid).toBe(false);
});
it('should invalidate the current content script when a new context is created', async () => {
const name = 'test';
const onInvalidated = vi.fn();
const ctx = new ContentScriptContext(name);
ctx.onInvalidated(onInvalidated);
// Wait for events to run before next tick next tick
await sleep(0);
// Create a new context after first is initialized, and wait for it to initialize
new ContentScriptContext(name);
await sleep(0);
expect(onInvalidated).toBeCalled();
expect(ctx.isValid).toBe(false);
});
it('should not invalidate the current content script when a new context is created with a different name', async () => {
const onInvalidated = vi.fn();
const ctx = new ContentScriptContext('test1');
ctx.onInvalidated(onInvalidated);
// Wait for events to run before next tick next tick
await sleep(0);
// Create a new context after first is initialized, and wait for it to initialize
new ContentScriptContext('test2');
await sleep(0);
expect(onInvalidated).not.toBeCalled();
expect(ctx.isValid).toBe(true);
});
});
@@ -46,13 +46,13 @@ export class ContentScriptContext implements AbortController {
public readonly options?: Omit<ContentScriptDefinition, 'main'>,
) {
this.#abortController = new AbortController();
if (this.#isTopFrame) {
this.#listenForNewerScripts({ ignoreFirstEvent: true });
this.#stopOldScripts();
}
this.setTimeout(() => {
// Run on next tick so the listener it adds isn't triggered by stopOldScript
} else {
this.#listenForNewerScripts();
});
}
}
get signal() {
@@ -224,12 +224,18 @@ export class ContentScriptContext implements AbortController {
);
}
#listenForNewerScripts() {
#listenForNewerScripts(options?: { ignoreFirstEvent?: boolean }) {
let isFirst = true;
const cb = (event: MessageEvent) => {
if (
event.data?.type === ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE &&
event.data?.contentScriptName === this.contentScriptName
) {
const wasFirst = isFirst;
isFirst = false;
if (wasFirst && options?.ignoreFirstEvent) return;
this.notifyInvalidated();
}
};
@@ -62,6 +62,7 @@ export function createIframeUi<TMounted>(
const wrapper = document.createElement('div');
wrapper.setAttribute('data-wxt-iframe', '');
const iframe = document.createElement('iframe');
// @ts-expect-error: getURL is defined per-project, but not inside the package
iframe.src = browser.runtime.getURL(options.page);
wrapper.appendChild(iframe);
@@ -258,6 +259,7 @@ function mountUi(
* Load the CSS for the current entrypoint.
*/
async function loadCss(): Promise<string> {
// @ts-expect-error: getURL is defined per-project, but not inside the package
const url = browser.runtime.getURL(
`/content-scripts/${import.meta.env.ENTRYPOINT}.css`,
);
@@ -83,7 +83,8 @@ export type IframeContentScriptUiOptions<TMounted> =
* The path to the HTML page that will be shown in the iframe. This string is passed into
* `browser.runtime.getURL`.
*/
page: PublicPath;
// @ts-expect-error: HtmlPublicPath is generated per-project
page: import('wxt/browser').HtmlPublicPath;
/**
* Callback executed when mounting the UI. Use this function to customize the iframe or wrapper
* element's appearance. It is called every time `ui.mount()` is called.
+1
View File
@@ -5,3 +5,4 @@
*/
export * from './content-scripts';
export * from './app-config';
export * from './inject-script';
+47
View File
@@ -0,0 +1,47 @@
import { browser } from 'wxt/browser';
export type ScriptPublicPath = Extract<
// @ts-expect-error: PublicPath is generated per-project
import('wxt/browser').PublicPath,
`${string}.js`
>;
/**
* This function can only be called inside content scripts.
*
* Inject an unlisted script into the page. Scripts are added to the `<head>`
* element or `document.documentElement` if there is no head.
*
* Make sure to add the injected script to your manifest's
* `web_accessible_resources`.
*/
export async function injectScript(
path: ScriptPublicPath,
options?: InjectScriptOptions,
): Promise<void> {
// @ts-expect-error: getURL is defined per-project, but not inside the package
const url = browser.runtime.getURL(path);
const script = document.createElement('script');
if (browser.runtime.getManifest().manifest_version === 2) {
// MV2 requires using an inline script
script.innerHTML = await fetch(url).then((res) => res.text());
} else {
// MV3 requires using src
script.src = url;
}
if (!options?.keepInDom) {
script.onload = () => script.remove();
}
(document.head ?? document.documentElement).append(script);
}
export interface InjectScriptOptions {
/**
* By default, the injected script is removed from the DOM after being
* injected. To disable this behavior, set this flag to true.
*/
keepInDom?: boolean;
}
+1 -4
View File
@@ -230,10 +230,7 @@ export async function createViteBuilder(
baseConfig.optimizeDeps.noDiscovery = true;
baseConfig.optimizeDeps.include = [];
const envConfig: vite.InlineConfig = {
plugins: [
wxtPlugins.extensionApiMock(wxtConfig),
wxtPlugins.removeEntrypointMainFunction(wxtConfig, path),
],
plugins: [wxtPlugins.removeEntrypointMainFunction(wxtConfig, path)],
};
const config = vite.mergeConfig(baseConfig, envConfig);
const server = await vite.createServer(config);
@@ -14,8 +14,17 @@ export function removeEntrypointMainFunction(
const absPath = normalizePath(resolve(config.root, path));
return {
name: 'wxt:remove-entrypoint-main-function',
transform(code, id) {
if (id === absPath) return removeMainFunctionCode(code);
transform: {
order: 'pre',
handler(code, id) {
if (id === absPath) {
const newCode = removeMainFunctionCode(code);
config.logger.debug('vite-node transformed entrypoint', path);
config.logger.debug(`Original:\n---\n${code}\n---`);
config.logger.debug(`Transformed:\n---\n${newCode.code}\n---`);
return newCode;
}
},
},
};
}
@@ -1,34 +1,41 @@
lockfileVersion: '6.0'
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
mime-types:
specifier: 2.1.35
version: 2.1.35
importers:
devDependencies:
flatten:
specifier: 1.0.3
version: 1.0.3
.:
dependencies:
mime-types:
specifier: 2.1.35
version: 2.1.35
devDependencies:
flatten:
specifier: 1.0.3
version: 1.0.3
packages:
/flatten@1.0.3:
flatten@1.0.3:
resolution: {integrity: sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==}
deprecated: flatten is deprecated in favor of utility frameworks such as lodash.
dev: true
/mime-db@1.52.0:
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: false
/mime-types@2.1.35:
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
snapshots:
flatten@1.0.3: {}
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
dev: false
@@ -6,23 +6,28 @@ describe('Transform Utils', () => {
it.each(['defineBackground', 'defineUnlistedScript'])(
'should remove the first arrow function argument for %s',
(def) => {
const input = `export default ${def}(() => {
const input = `
export default ${def}(() => {
console.log();
})`;
const expected = `export default ${def}(() => {})`;
})
`;
const expected = `export default ${def}();`;
const actual = removeMainFunctionCode(input).code;
expect(actual).toEqual(expected);
},
);
it.each(['defineBackground', 'defineUnlistedScript'])(
'should remove the first function argument for %s',
(def) => {
const input = `export default ${def}(function () {
const input = `
export default ${def}(function () {
console.log();
})`;
const expected = `export default ${def}(function () {})`;
})
`;
const expected = `export default ${def}();`;
const actual = removeMainFunctionCode(input).code;
@@ -35,10 +40,12 @@ describe('Transform Utils', () => {
'defineContentScript',
'defineUnlistedScript',
])('should remove the main field from %s', (def) => {
const input = `export default ${def}({
asdf: "asdf",
main: () => {},
})`;
const input = `
export default ${def}({
asdf: "asdf",
main: () => {},
})
`;
const expected = `export default ${def}({
asdf: "asdf"
})`;
@@ -47,5 +54,88 @@ describe('Transform Utils', () => {
expect(actual).toEqual(expected);
});
it('should remove unused imports', () => {
const input = `
import { defineBackground } from "wxt/sandbox"
import { test1 } from "somewhere1"
import test2 from "somewhere2"
export default defineBackground(() => {})
`;
const expected = `import { defineBackground } from "wxt/sandbox"
export default defineBackground();`;
const actual = removeMainFunctionCode(input).code;
expect(actual).toEqual(expected);
});
it('should remove explict side-effect imports', () => {
const input = `
import { defineBackground } from "wxt/sandbox"
import "my-polyfill"
import "./style.css"
export default defineBackground(() => {})
`;
const expected = `import { defineBackground } from "wxt/sandbox"
export default defineBackground();`;
const actual = removeMainFunctionCode(input).code;
expect(actual).toEqual(expected);
});
it("should remove any functions delcared outside the main function that aren't used", () => {
const input = `
function getMatches() {
return ["*://*/*"]
}
function unused1() {}
function unused2() {
unused1();
}
export default defineContentScript({
matches: getMatches(),
main: () => {},
})
`;
const expected = `function getMatches() {
return ["*://*/*"]
}
export default defineContentScript({
matches: getMatches()
})`;
const actual = removeMainFunctionCode(input).code;
expect(actual).toEqual(expected);
});
it("should remove any variables delcared outside the main function that aren't used", () => {
const input = `
const unused1 = "a", matches = ["*://*/*"];
let unused2 = unused1 + "b";
export default defineContentScript({
matches,
main: () => {}
})
`;
const expected = `const matches = ["*://*/*"];
export default defineContentScript({
matches
})`;
const actual = removeMainFunctionCode(input).code;
expect(actual).toEqual(expected);
});
});
});
@@ -28,6 +28,7 @@ import { VIRTUAL_NOOP_BACKGROUND_MODULE_ID } from '../../utils/constants';
import { CSS_EXTENSIONS_PATTERN } from '../../utils/paths';
import pc from 'picocolors';
import { wxt } from '../../wxt';
import { createExtensionEnvironment } from '../environments';
/**
* Return entrypoints and their configuration by looking through the project's files.
@@ -35,7 +36,17 @@ import { wxt } from '../../wxt';
export async function findEntrypoints(): Promise<Entrypoint[]> {
// Make sure required TSConfig file exists to load dependencies
await fs.mkdir(wxt.config.wxtDir, { recursive: true });
await fs.writeJson(resolve(wxt.config.wxtDir, 'tsconfig.json'), {});
try {
await fs.writeJson(
resolve(wxt.config.wxtDir, 'tsconfig.json'),
{},
{ flag: 'wx' },
);
} catch (err) {
if (!(err instanceof Error) || !('code' in err) || err.code !== 'EEXIST') {
throw err;
}
}
const relativePaths = await glob(Object.keys(PATH_GLOB_TO_TYPE_MAP), {
cwd: wxt.config.entrypointsDir,
@@ -72,47 +83,50 @@ export async function findEntrypoints(): Promise<Entrypoint[]> {
// Import entrypoints to get their config
let hasBackground = false;
const entrypoints: Entrypoint[] = await Promise.all(
entrypointInfos.map(async (info): Promise<Entrypoint> => {
const { type } = info;
switch (type) {
case 'popup':
return await getPopupEntrypoint(info);
case 'sidepanel':
return await getSidepanelEntrypoint(info);
case 'options':
return await getOptionsEntrypoint(info);
case 'background':
hasBackground = true;
return await getBackgroundEntrypoint(info);
case 'content-script':
return await getContentScriptEntrypoint(info);
case 'unlisted-page':
return await getUnlistedPageEntrypoint(info);
case 'unlisted-script':
return await getUnlistedScriptEntrypoint(info);
case 'content-script-style':
return {
...info,
type,
outputDir: resolve(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
options: {
include: undefined,
exclude: undefined,
},
};
default:
return {
...info,
type,
outputDir: wxt.config.outDir,
options: {
include: undefined,
exclude: undefined,
},
};
}
}),
const env = createExtensionEnvironment();
const entrypoints: Entrypoint[] = await env.run(() =>
Promise.all(
entrypointInfos.map(async (info): Promise<Entrypoint> => {
const { type } = info;
switch (type) {
case 'popup':
return await getPopupEntrypoint(info);
case 'sidepanel':
return await getSidepanelEntrypoint(info);
case 'options':
return await getOptionsEntrypoint(info);
case 'background':
hasBackground = true;
return await getBackgroundEntrypoint(info);
case 'content-script':
return await getContentScriptEntrypoint(info);
case 'unlisted-page':
return await getUnlistedPageEntrypoint(info);
case 'unlisted-script':
return await getUnlistedScriptEntrypoint(info);
case 'content-script-style':
return {
...info,
type,
outputDir: resolve(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
options: {
include: undefined,
exclude: undefined,
},
};
default:
return {
...info,
type,
outputDir: wxt.config.outDir,
options: {
include: undefined,
exclude: undefined,
},
};
}
}),
),
);
if (wxt.config.command === 'serve' && !hasBackground) {
@@ -94,9 +94,9 @@ async function getPathsDeclarationEntry(
.join('\n');
const template = `// Generated by wxt
import "${wxt.config.browserModule}";
import "wxt/browser";
declare module "${wxt.config.browserModule}" {
declare module "wxt/browser" {
export type PublicPath =
{{ union }}
type HtmlPublicPath = Extract<PublicPath, \`\${string}.html\`>
@@ -117,9 +117,9 @@ declare module "${wxt.config.browserModule}" {
async function getI18nDeclarationEntry(): Promise<WxtDirFileEntry> {
const defaultLocale = wxt.config.manifest.default_locale;
const template = `// Generated by wxt
import "${wxt.config.browserModule}";
import "wxt/browser";
declare module "${wxt.config.browserModule}" {
declare module "wxt/browser" {
/**
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
*/
@@ -183,8 +183,6 @@ export async function resolveConfig(
userConfigMetadata: userConfigMetadata ?? {},
alias,
extensionApi,
browserModule:
extensionApi === 'chrome' ? 'wxt/browser/chrome' : 'wxt/browser',
entrypointLoader: mergedConfig.entrypointLoader ?? 'vite-node',
experimental: defu(mergedConfig.experimental, {}),
dev: {
@@ -0,0 +1,21 @@
import { parseHTML } from 'linkedom';
import { createEnvironment, Environment, EnvGlobals } from './environment';
export function createBrowserEnvironment(): Environment {
return createEnvironment(getBrowserEnvironmentGlobals);
}
export function getBrowserEnvironmentGlobals(): EnvGlobals {
const { window, document, global } = parseHTML(`
<html>
<head></head>
<body></body>
</html>
`);
return {
...global,
window,
document,
self: global,
};
}
@@ -0,0 +1,49 @@
export interface Environment {
setup: () => () => void;
run: <T>(fn: () => Promise<T>) => Promise<T>;
}
export function createEnvironment(getGlobals: () => EnvGlobals): Environment {
const setup = () => {
const envGlobals = getGlobals();
const ogGlobals = getOgGlobals(envGlobals);
applyGlobals(envGlobals);
return () => {
applyGlobals(ogGlobals);
};
};
const run = async (fn: () => any) => {
const teardown = setup();
try {
return await fn();
} finally {
teardown();
}
};
return {
setup,
run,
};
}
export type EnvGlobals = Record<string, any>;
export function getOgGlobals(envGlobals: EnvGlobals): EnvGlobals {
return Object.keys(envGlobals).reduce<typeof envGlobals>((acc, key) => {
// @ts-expect-error: Untyped key on globalThis
acc[key] = globalThis[key];
return acc;
}, {});
}
export function applyGlobals(globals: EnvGlobals): void {
Object.entries(globals).forEach(([key, envValue]) => {
try {
// @ts-expect-error: Untyped key on globalThis
globalThis[key] = envValue;
} catch (err) {
// ignore any globals that can't be set
}
});
}
@@ -0,0 +1,15 @@
import { fakeBrowser } from '@webext-core/fake-browser';
import { getBrowserEnvironmentGlobals } from './browser-environment';
import { createEnvironment, Environment } from './environment';
export function createExtensionEnvironment(): Environment {
return createEnvironment(getExtensionEnvironmentGlobals);
}
export function getExtensionEnvironmentGlobals() {
return {
...getBrowserEnvironmentGlobals(),
chrome: fakeBrowser,
browser: fakeBrowser,
};
}
@@ -0,0 +1,2 @@
export * from './browser-environment';
export * from './extension-environment';
@@ -297,7 +297,6 @@ export const fakeResolvedConfig = fakeObjectCreator<ResolvedConfig>(() => {
userConfigMetadata: {},
alias: {},
extensionApi: 'webextension-polyfill',
browserModule: 'wxt/browser',
entrypointLoader: 'vite-node',
experimental: {},
dev: {
+146 -6
View File
@@ -2,8 +2,10 @@ import { ProxifiedModule, parseModule } from 'magicast';
/**
* Removes any code used at runtime related to an entrypoint's main function.
* - Removes or clears out `main` function from returned object
* - TODO: Removes unused imports after main function has been removed to prevent importing runtime modules
* 1. Removes or clears out `main` function from returned object
* 2. Removes any unused functions/variables outside the definition that aren't being called/used
* 3. Removes unused imports
* 3. Removes value-less, side-effect only imports (like `import "./styles.css"` or `import "webextension-polyfill"`)
*/
export function removeMainFunctionCode(code: string): {
code: string;
@@ -11,16 +13,26 @@ export function removeMainFunctionCode(code: string): {
} {
const mod = parseModule(code);
emptyMainFunction(mod);
let removedCount = 0;
let depth = 0;
const maxDepth = 10;
do {
removedCount = 0;
removedCount += removeUnusedTopLevelVariables(mod);
removedCount += removeUnusedTopLevelFunctions(mod);
removedCount += removeUnusedImports(mod);
} while (removedCount > 0 && depth++ <= maxDepth);
removeSideEffectImports(mod);
return mod.generate();
}
function emptyMainFunction(mod: ProxifiedModule) {
function emptyMainFunction(mod: ProxifiedModule): void {
if (mod.exports?.default?.$type === 'function-call') {
if (mod.exports.default.$ast?.arguments?.[0]?.body) {
// Remove body from function
// ex: "fn(() => { ... })" to "fn(() => {})"
// ex: "fn(function () { ... })" to "fn(function () {})"
mod.exports.default.$ast.arguments[0].body.body = [];
// ex: "fn(() => { ... })" to "fn()"
// ex: "fn(function () { ... })" to "fn()"
delete mod.exports.default.$ast.arguments[0];
} else if (mod.exports.default.$ast?.arguments?.[0]?.properties) {
// Remove main field from object
// ex: "fn({ ..., main: () => {} })" to "fn({ ... })"
@@ -31,3 +43,131 @@ function emptyMainFunction(mod: ProxifiedModule) {
}
}
}
function removeUnusedTopLevelVariables(mod: ProxifiedModule): number {
const simpleAst = getSimpleAstJson(mod.$ast);
const usedMap = findUsedIdentifiers(simpleAst);
let deletedCount = 0;
const ast = mod.$ast as any;
for (let i = ast.body.length - 1; i >= 0; i--) {
if (ast.body[i].type === 'VariableDeclaration') {
for (let j = ast.body[i].declarations.length - 1; j >= 0; j--) {
if (!usedMap.get(ast.body[i].declarations[j].id.name)) {
ast.body[i].declarations.splice(j, 1);
deletedCount++;
}
}
if (ast.body[i].declarations.length === 0) {
ast.body.splice(i, 1);
}
}
}
return deletedCount;
}
function removeUnusedTopLevelFunctions(mod: ProxifiedModule): number {
const simpleAst = getSimpleAstJson(mod.$ast);
const usedMap = findUsedIdentifiers(simpleAst);
let deletedCount = 0;
const ast = mod.$ast as any;
for (let i = ast.body.length - 1; i >= 0; i--) {
if (
ast.body[i].type === 'FunctionDeclaration' &&
!usedMap.get(ast.body[i].id.name)
) {
ast.body.splice(i, 1);
deletedCount++;
}
}
return deletedCount;
}
function removeUnusedImports(mod: ProxifiedModule): number {
const simpleAst = getSimpleAstJson(mod.$ast);
const usedMap = findUsedIdentifiers(simpleAst);
const importSymbols = Object.keys(mod.imports);
let deletedCount = 0;
importSymbols.forEach((name) => {
if (usedMap.get(name)) return;
delete mod.imports[name];
deletedCount++;
});
return deletedCount;
}
// TODO: Do a more complex declaration analysis where shadowed variables are detected and ignored.
// Right now, this code assumes there are no shadowed variables.
function findUsedIdentifiers(simpleAst: any) {
const usedMap = new Map<string, boolean>();
const queue: any[] = [simpleAst];
for (const item of queue) {
if (!item) {
} else if (Array.isArray(item)) {
queue.push(...item);
} else if (item.type === 'ImportDeclaration') {
// Don't look inside imports, identifiers are only used for declaration
continue;
} else if (item.type === 'Identifier') {
usedMap.set(item.name, true);
} else if (typeof item === 'object') {
const filterFns: Record<string, (entry: [string, any]) => boolean> = {
// Ignore the function declaration's name
FunctionDeclaration: ([key]) => key !== 'id',
// Ignore object property names
ObjectProperty: ([key]) => key !== 'key',
// Ignore variable declaration's name
VariableDeclarator: ([key]) => key !== 'id',
};
queue.push(
Object.entries(item)
.filter(filterFns[item.type] ?? (() => true))
.map(([_, value]) => value),
);
}
}
return usedMap;
}
function deleteImportAst(
mod: ProxifiedModule,
shouldDelete: (node: any) => boolean,
): void {
const importIndexesToDelete: number[] = [];
(mod.$ast as any).body.forEach((node: any, index: number) => {
if (node.type === 'ImportDeclaration' && shouldDelete(node)) {
importIndexesToDelete.push(index);
}
});
importIndexesToDelete.reverse().forEach((i) => {
delete (mod.$ast as any).body[i];
});
}
function removeSideEffectImports(mod: ProxifiedModule): void {
deleteImportAst(mod, (node) => node.specifiers.length === 0);
}
/**
* Util to get the AST as a simple JSON object, stripping out large objects and file locations to keep it readible
*/
function getSimpleAstJson(ast: any): any {
if (!ast) {
return ast;
} else if (Array.isArray(ast)) {
return ast.map(getSimpleAstJson);
} else if (typeof ast === 'object') {
return Object.fromEntries(
Object.entries(ast)
.filter(
([key, value]) => key !== 'loc' && key !== 'start' && key !== 'end',
)
.map(([key, value]) => [key, getSimpleAstJson(value)]),
);
} else {
return ast;
}
}
+4
View File
@@ -27,6 +27,10 @@ export async function registerWxt(
inlineConfig: InlineConfig = {},
getServer?: (config: ResolvedConfig) => Promise<WxtDevServer>,
): Promise<void> {
// Default NODE_ENV environment variable before other packages, like vite, do it
// See https://github.com/wxt-dev/wxt/issues/873#issuecomment-2254555523
process.env.NODE_ENV ??= command === 'serve' ? 'development' : 'production';
const hooks = createHooks<WxtHooks>();
const config = await resolveConfig(inlineConfig, command);
const server = await getServer?.(config);
@@ -7,6 +7,6 @@ export function defineBackground(
export function defineBackground(
arg: (() => void) | BackgroundDefinition,
): BackgroundDefinition {
if (typeof arg === 'function') return { main: arg };
if (arg == null || typeof arg === 'function') return { main: arg };
return arg;
}
@@ -9,6 +9,6 @@ export function defineUnlistedScript(
export function defineUnlistedScript(
arg: (() => void) | UnlistedScriptDefinition,
): UnlistedScriptDefinition {
if (typeof arg === 'function') return { main: arg };
if (arg == null || typeof arg === 'function') return { main: arg };
return arg;
}
+10 -2
View File
@@ -1046,9 +1046,14 @@ export interface WxtHooks {
* // variable called "example" in the TS project.
* entries.push({
* path: "types/example.d.ts",
* textContent: "declare const a: string;",
* text: "declare const a: string;",
* tsReference: true,
* });
* // use module to add Triple-Slash Directive in .wxt/wxt.d.ts
* // eg: /// <reference types="@types/example" />
* entries.push({
* module: '@types/example'
* });
* })
*/
'prepare:types': (wxt: Wxt, entries: WxtDirEntry[]) => HookResult;
@@ -1210,7 +1215,6 @@ export interface ResolvedConfig {
*/
alias: Record<string, string>;
extensionApi: 'webextension-polyfill' | 'chrome';
browserModule: 'wxt/browser' | 'wxt/browser/chrome';
entrypointLoader: 'vite-node' | 'jiti';
experimental: {};
dev: {
@@ -1409,6 +1413,10 @@ export type WxtDirEntry = WxtDirTypeReferenceEntry | WxtDirFileEntry;
* Represents type reference to a node module to be added to `.wxt/wxt.d.ts` file
*/
export interface WxtDirTypeReferenceEntry {
/**
* Specifies the module name that will be used in the `/// <reference types="..." />` directive.
* This value will be added to the `.wxt/wxt.d.ts` file to include type definitions from the specified module.
*/
module: string;
}
+1 -4
View File
@@ -18,10 +18,7 @@ export default defineConfig({
ignored: '**/dist/**',
},
},
plugins: [
// @ts-expect-error: Vite version mismatch
RandomSeed(),
],
plugins: [RandomSeed()],
resolve: {
alias: {
'wxt/testing': path.resolve('src/testing'),
+5406 -4087
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -27,7 +27,8 @@ const rawCommits = await listCommitsInDir(pkgDir, prevTag);
const commits = parseCommits(rawCommits, config);
// Bump version
let bumpType = determineSemverChange(commits, config) ?? 'patch';
const originalBumpType = determineSemverChange(commits, config) ?? 'patch';
let bumpType = originalBumpType;
if (currentVersion.startsWith('0.')) {
if (bumpType === 'major') {
bumpType = 'minor';
@@ -54,7 +55,7 @@ let versionChangelogBody = versionChangelog
.slice(1)
.join('\n')
.trim();
if (bumpType === 'major') {
if (originalBumpType === 'major') {
versionChangelogBody = versionChangelogBody.replace(
'[compare changes]',
`[⚠️ breaking changes](https://wxt.dev/guide/upgrade-guide/wxt) &bull; [compare changes]`,
+3 -3
View File
@@ -1,5 +1,5 @@
{
"name": "wxt-starter",
"name": "wxt-react-starter",
"description": "manifest.json description",
"private": true,
"version": "0.0.0",
@@ -22,7 +22,7 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@wxt-dev/module-react": "^1.1.0",
"typescript": "^5.5.3",
"wxt": "^0.18.15"
"typescript": "^5.5.4",
"wxt": "^0.19.1"
}
}
+4 -4
View File
@@ -1,5 +1,5 @@
{
"name": "wxt-starter",
"name": "wxt-solid-starter",
"description": "manifest.json description",
"private": true,
"version": "0.0.0",
@@ -15,11 +15,11 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"solid-js": "^1.8.18"
"solid-js": "^1.8.19"
},
"devDependencies": {
"@wxt-dev/module-solid": "^1.1.1",
"typescript": "^5.5.3",
"wxt": "^0.18.15"
"typescript": "^5.5.4",
"wxt": "^0.19.1"
}
}
+3 -3
View File
@@ -1,5 +1,5 @@
{
"name": "wxt-starter",
"name": "wxt-svelte-starter",
"description": "manifest.json description",
"private": true,
"version": "0.0.0",
@@ -19,7 +19,7 @@
"svelte": "^4.2.18",
"svelte-check": "^3.8.4",
"tslib": "^2.6.3",
"typescript": "^5.5.3",
"wxt": "^0.18.15"
"typescript": "^5.5.4",
"wxt": "^0.19.1"
}
}
+2 -2
View File
@@ -15,7 +15,7 @@
"postinstall": "wxt prepare"
},
"devDependencies": {
"typescript": "^5.5.3",
"wxt": "^0.18.15"
"typescript": "^5.5.4",
"wxt": "^0.19.1"
}
}
+5 -5
View File
@@ -1,5 +1,5 @@
{
"name": "wxt-starter",
"name": "wxt-vue-starter",
"description": "manifest.json description",
"private": true,
"version": "0.0.0",
@@ -15,12 +15,12 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"vue": "^3.4.33"
"vue": "^3.4.34"
},
"devDependencies": {
"@wxt-dev/module-vue": "^1.0.0",
"typescript": "^5.5.3",
"vue-tsc": "^2.0.26",
"wxt": "^0.18.15"
"typescript": "^5.5.4",
"vue-tsc": "^2.0.29",
"wxt": "^0.19.1"
}
}