Compare commits

...

48 Commits

Author SHA1 Message Date
GitHub Actions b4283e37bd chore(release): wxt v0.18.8 2024-06-20 14:03:16 +00:00
Aaron b467ad760a fix(modules): addViteConfig ignored user vite config (#760) 2024-06-20 08:59:46 -05:00
Aaron 9fc6408ef9 chore: Add E2E test for addImportPreset 2024-06-20 08:52:45 -05:00
Aaron 394cb1a520 docs: Add 'WorkFlowy MultiFlow' to homepage 2024-06-19 14:16:58 -05:00
Aaron ffec88549b fix: Ignore .wxt file changes in dev (#755) 2024-06-19 08:33:52 -05:00
Ziheng Xun 4758bea187 docs: update sandbox.md (#754) 2024-06-19 08:06:08 -05:00
Aaron 6671471f75 feat(dev): Reload extension when public files change (#752) 2024-06-19 08:05:56 -05:00
Aaron ae78dc7fb3 chore: Refactor client web socket util (#753) 2024-06-18 22:47:30 -05:00
Aaron b62af3d007 chore(dev-deps): Upgrade @aklinker1/buildc to 1.0.7 (#748) 2024-06-18 00:11:14 -05:00
Aaron 43ae44ecd5 fix: Don't load plugins twice in HTML pages (#746) 2024-06-17 23:41:39 -05:00
Aaron 5bab9d2f59 chore: Fix build script flags (#745) 2024-06-17 23:41:10 -05:00
Aaron 7edf1c8103 fix: Upgrade wxt peer to >= 0.18.6
This closes https://github.com/wxt-dev/wxt/issues/743
2024-06-17 14:42:24 -05:00
Aaron 53b4eac955 docs: Update wxt init command for bun 2024-06-17 14:39:41 -05:00
GitHub Actions f9a10c2c52 chore(release): wxt v0.18.7 2024-06-17 16:17:42 +00:00
Aaron 8206ca9164 fix(zip): Revert dotfile changes from #674 (#742) 2024-06-17 11:10:06 -05:00
Aaron 1a823cc008 chore: Extract build cache script to NPM package (#737) 2024-06-17 11:09:18 -05:00
Aaron efc011eb81 ci: Fix release 422 failure 2024-06-14 00:36:03 -05:00
GitHub Actions 35f6d28977 chore(release): @wxt-dev/module-solid v1.0.1 2024-06-14 05:29:40 +00:00
Aaron 43fcbec0eb fix: Add target: esnext by default (#733) 2024-06-14 00:25:11 -05:00
Aaron a0d56b4d27 chore: Update templates to use new modules (#732) 2024-06-14 00:19:13 -05:00
Aaron 38ccb6ace6 docs: Update frontend framework docs with new modules (#731) 2024-06-14 00:06:23 -05:00
Aaron aaf75f1de1 ci: Only mark wxt releases as latest 2024-06-14 00:02:41 -05:00
Aaron 21e8ca0a2c chore: Add changelog 2024-06-13 23:52:58 -05:00
Aaron 55444858bc ci: Add new modules as release targets 2024-06-13 23:44:28 -05:00
Aaron 771ea03069 feat: Add @wxt-dev/module-solid package (#730) 2024-06-13 23:23:32 -05:00
Aaron 38b71d1350 chore: Remove extra dependencies 2024-06-13 23:08:17 -05:00
Aaron cb781aceb5 chore: Remove extra dependencies 2024-06-13 23:05:48 -05:00
Aaron d53c792218 chore: Remove dependencies from vue module 2024-06-13 23:03:16 -05:00
Aaron 907f4d3672 feat: Add @wxt-dev/module-react package (#729) 2024-06-13 22:53:40 -05:00
Aaron 8432d73a6b fix: Generalize react-refresh preamble logic to virtualize all inline scripts (#728) 2024-06-13 19:17:23 -05:00
Aaron 94a5435089 docs: Add missing vue install step 2024-06-13 16:39:09 -05:00
Aaron edf3cf3f17 feat: Add @wxt-dev/module-vue package (#726) 2024-06-13 16:36:37 -05:00
Aaron 48783baec3 chore(deps): Upgrade and sync all dependencies (#725) 2024-06-13 15:44:00 -05:00
Aaron c70f047696 chore: Add taze to project (#724) 2024-06-13 15:17:21 -05:00
Aaron e6e0c713bc feat: Add @wxt-dev/module-vue package (#716) 2024-06-13 15:16:45 -05:00
Aaron 3691554a73 docs: Update contributor docs with release steps 2024-06-13 14:58:57 -05:00
Aaron 1a3436606b fix(dev): Fix CSP error loading HTML plugins (#723) 2024-06-13 14:49:50 -05:00
GitHub Actions 2c9e7750ec chore(release): wxt v0.18.6 2024-06-13 17:28:34 +00:00
Aaron fa67910007 fix(modules): Add modules to TS project so type augmentation works (#722) 2024-06-13 12:25:18 -05:00
Aaron c77773eb20 feat(modules): Add new addImportPreset helper function (#720) 2024-06-13 12:20:09 -05:00
Aaron 148f3d53a6 fix(modules): Re-export WxtModule type from wxt/modules to prevent TS2742 (#721) 2024-06-13 12:19:15 -05:00
Aaron c7af789173 fix(types): Module options param is optional, but types did not reflect that (#719) 2024-06-13 11:20:59 -05:00
Aaron 274d39b9a4 chore(dev-deps): Upgrade typescript to 5.4 (#718) 2024-06-13 00:38:11 -05:00
Aaron d1edb2da0e chore: Cache workspace dependency builds (#715) 2024-06-12 22:34:08 -05:00
Aaron ed860027ef ci: Fix sync-releases script 2024-06-12 00:51:11 -05:00
Aaron 488fa6e773 ci: Fix sync-releases workflow 2024-06-12 00:48:40 -05:00
Aaron ea51cc04a9 Fix changelog 2024-06-12 00:46:23 -05:00
Aaron 6d1761533f ci: Fix compare url generation 2024-06-12 00:45:39 -05:00
71 changed files with 3244 additions and 1184 deletions
+4
View File
@@ -8,6 +8,10 @@ on:
type: choice
options:
- wxt
- module-react
- module-vue
- module-svelte
- module-solid
jobs:
validate:
+5 -5
View File
@@ -8,17 +8,17 @@ on:
type: choice
options:
- wxt
- module-react
- module-vue
- module-svelte
- module-solid
jobs:
sync:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: pnpm
- uses: ./.github/actions/setup
- run: pnpm tsx scripts/sync-releases.ts ${{ inputs.package }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+6
View File
@@ -13,6 +13,12 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm check
builds:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm buildc all
build-demo:
runs-on: ubuntu-22.04
steps:
+1
View File
@@ -20,3 +20,4 @@ templates/*/package-lock.json
docs/api/reference
stats.html
.tool-versions
.cache
+17
View File
@@ -112,3 +112,20 @@ cp -r templates/vanilla templates/<new-template-name>
```
That's it. Once your template is merged, it will be available inside `wxt init` immediately. You don't need to release a new version of WXT to release a new template.
## Releasing Updates
Releases are done with GitHub actions:
- Use the [Release workflow](https://github.com/wxt-dev/wxt/actions/workflows/release.yml) to release a single package in the monorepo. This automatically detects the version change with conventional commits, builds and uploads the package to NPM, and creates a GitHub release.
- Use the [Sync Releases workflow](https://github.com/wxt-dev/wxt/actions/workflows/sync-releases.yml) to sync the GitHub releases with changes to the changelog. To change a release, update the `CHANGELOG.md` file and run the workflow. It will sync the releases of a single package in the monorepo.
## Upgrading Dependencies
Use [`taze`](https://www.npmjs.com/package/taze) to upgrade dependencies throughout the entire monorepo.
```ts
pnpm dlx taze -r
```
Configuration is in [`taze.config.ts`](./taze.config.ts).
@@ -29,6 +29,7 @@ const chromeExtensionIds = [
'nedcanggplmbbgmlpcjiafgjcpdimpea', // YTBlock - Block any content from YouTube™
'oadbjpccljkplmhnjekgjamejnbadlne', // demo.fun - Interactive product demos that convert
'iopdafdcollfgaoffingmahpffckmjni', // SmartEReply: Elevate Your LinkedIn™ Engagement with AI 🚀📈
'khjdmjcmpolknpccmaaipmidphjokhdf', // WorkFlowy MultiFlow
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
+8 -2
View File
@@ -15,7 +15,14 @@ npx wxt@latest init <project-name>
```
```sh [bun]
bunx wxt@latest init <project-name>
# The "wxt init" command currently fails when ran with bunx.
# Use NPX as a workaround, and select "bun" as your package
# manager. To stay up to date with this issue, follow
# https://github.com/wxt-dev/wxt/issues/707
#
# bunx wxt@latest init <project-name>
npx wxt@latest init <project-name>
```
:::
@@ -96,7 +103,6 @@ pnpm dev
```
:::tip 🎉&ensp;Well done!
The dev command will build the extension for development, open the browser, and reload the different parts of the extension when you save changes.
:::
@@ -12,8 +12,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'],
]"
/>
+49 -29
View File
@@ -1,28 +1,59 @@
# Frontend Frameworks
WXT supports all frontend frameworks with a Vite plugin:
## Built-in Modules
- `@vitejs/plugin-vue`
- `@vitejs/plugin-react`
- `@vitejs/plugin-react-swc`
- And more!
WXT has preconfigured modules for 4 frameworks:
Just add the vite plugin to your config and you're good to go! Use the framework in HTML pages or content scripts, it will just work 👍
- [`@wxt-dev/module-react`](https://github.com/wxt-dev/wxt/tree/main/packages/module-react)
- [`@wxt-dev/module-vue`](https://github.com/wxt-dev/wxt/tree/main/packages/module-vue)
- [`@wxt-dev/module-svelte`](https://github.com/wxt-dev/wxt/tree/main/packages/module-svelte)
- [`@wxt-dev/module-solid`](https://github.com/wxt-dev/wxt/tree/main/packages/module-solid)
Install the module for your framework, then add it to your config:
:::code-group
```ts [Vue]
```ts [React]
import { defineConfig } from 'wxt';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
vite: () => ({
plugins: [vue()],
}),
modules: ['@wxt-dev/module-react'],
});
```
```ts [React]
```ts [Vue]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-vue'],
});
```
```ts [Svelte]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-svelte'],
});
```
```ts [Solid]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-solid'],
});
```
:::
## Adding Vite Plugins
If your framework doesn't have an official WXT module, no worries! WXT supports any framework with a Vite plugin.
Just add the Vite plugin to your config and you're good to go! Use the framework in HTML pages or content scripts, it will just work 👍
```ts
import { defineConfig } from 'wxt';
import react from '@vitejs/plugin-react';
@@ -33,18 +64,7 @@ export default defineConfig({
});
```
```ts [Svelte]
import { defineConfig } from 'wxt';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
vite: () => ({
plugins: [svelte()],
}),
});
```
:::
The WXT modules just simplify the configuration and add auto-imports. They're not much different than the above.
## Multiple Apps
@@ -84,9 +104,9 @@ Lots of frameworks come with routers for building a multi-page app using the URL
Instead, you need to configure the router to run in "hash" mode, where the routing information is apart of the URL's hash, not the path (ie: `popup.html#/` and `popup.html#/account/settings`)
Refer to your router's docs for information about "hash" mode and how to enable it. Here's a non-extensive list of a few popular routers:
Refer to your router's docs for information about hash mode and how to enable it. Here's a non-extensive list of a few popular routers:
- [React](https://reactrouter.com/en/main/routers/create-hash-router)
- [Vue](https://router.vuejs.org/guide/essentials/history-mode.html#Hash-Mode)
- [Svelte](https://www.npmjs.com/package/svelte-spa-router#hash-based-routing)
- [Solid](https://github.com/solidjs/solid-router?tab=readme-ov-file#hash-mode-router)
- [`react-router`](https://reactrouter.com/en/main/routers/create-hash-router)
- [`vue-router`](https://router.vuejs.org/guide/essentials/history-mode.html#Hash-Mode)
- [`svelte-spa-router`](https://www.npmjs.com/package/svelte-spa-router#hash-based-routing)
- [`solid-router`](https://github.com/solidjs/solid-router?tab=readme-ov-file#hash-mode-router)
+21 -16
View File
@@ -5,9 +5,9 @@
"node": ">=18.20.3",
"pnpm": "^8.6.3"
},
"packageManager": "pnpm@8.6.3",
"packageManager": "pnpm@8.15.8",
"scripts": {
"check": "check && pnpm -r run check",
"check": "check && pnpm -r --sequential run check",
"test": "vitest",
"test:coverage": "vitest run --coverage.enabled \"--coverage.include=packages/wxt/src/**\" \"--coverage.exclude=packages/wxt/src/core/utils/testing/**\" \"--coverage.exclude=**/*.d.ts\" \"--coverage.exclude=**/fixtures/**\"",
"prepare": "simple-git-hooks",
@@ -18,28 +18,33 @@
"docs:preview": "pnpm -s docs:gen && vitepress preview docs"
},
"devDependencies": {
"@aklinker1/check": "^1.1.1",
"@aklinker1/buildc": "^1.0.7",
"@aklinker1/check": "^1.3.1",
"@types/fs-extra": "^11.0.4",
"@vitest/coverage-v8": "^1.0.1",
"@vitest/coverage-v8": "^1.6.0",
"changelogen": "^0.5.5",
"consola": "^3.2.3",
"execa": "^9.1.0",
"fs-extra": "^11.1.1",
"lint-staged": "^15.2.0",
"dependency-graph": "^1.0.0",
"execa": "^9.2.0",
"fast-glob": "^3.3.2",
"fs-extra": "^11.2.0",
"hasha": "^6.0.0",
"lint-staged": "^15.2.7",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.0",
"simple-git-hooks": "^2.9.0",
"tsx": "^4.11.2",
"prettier": "^3.3.2",
"simple-git-hooks": "^2.11.1",
"tsx": "^4.15.4",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "4.0.0-next.23",
"typedoc-vitepress-theme": "1.0.0-next.3",
"typescript": "^5.3.2",
"vitepress": "^1.2.2",
"vitest": "^1.5.3",
"typescript": "^5.4.5",
"vitepress": "^1.2.3",
"vitest": "^1.6.0",
"vitest-mock-extended": "^1.3.1",
"vitest-plugin-random-seed": "^1.0.2",
"vue": "^3.3.10",
"wxt": "workspace:*"
"vitest-plugin-random-seed": "^1.1.0",
"vue": "^3.4.27",
"wxt": "workspace:*",
"yaml": "^2.4.5"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
+5
View File
@@ -0,0 +1,5 @@
# Changelog
## v1.0.0
Initial release 🎉
+32
View File
@@ -0,0 +1,32 @@
# `@wxt-dev/module-react`
Enables the use of [React](https://react.dev/) in your web extension, in HTML pages and content scripts.
This plugin makes a few changes:
1. Adds `@vitejs/plugin-react` to vite
2. Adds the [`react` preset](https://github.com/unjs/unimport/blob/main/src/presets/react.ts) to auto-imports
## Usage
```sh
pnpm i react react-dom
pnpm i -D @wxt-dev/module-react
```
Then add the module to your config:
```ts
// wxt.config.ts
export default defineConfig({
// Required
modules: ['@wxt-dev/module-react'],
// Optional: Pass options to the module:
react: {
vite: {
// ...
},
},
});
```
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@wxt-dev/module-react",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": [
"dist"
],
"scripts": {
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
},
"peerDependencies": {
"wxt": ">=0.18.6"
},
"dependencies": {
"@vitejs/plugin-react": "^4.3.1"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
}
+27
View File
@@ -0,0 +1,27 @@
import 'wxt';
import { addImportPreset, addViteConfig, defineWxtModule } from 'wxt/modules';
import react, { Options as PluginOptions } from '@vitejs/plugin-react';
export default defineWxtModule<ReactModuleOptions>({
name: '@wxt-dev/module-react',
configKey: 'react',
setup(wxt, options) {
const { vite } = options ?? {};
addViteConfig(wxt, () => ({
plugins: [react(vite)],
}));
addImportPreset(wxt, 'react');
},
});
export interface ReactModuleOptions {
vite?: PluginOptions;
}
declare module 'wxt' {
export interface InlineConfig {
react?: ReactModuleOptions;
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"exclude": ["node_modules/**", "dist/**"]
}
+17
View File
@@ -0,0 +1,17 @@
# Changelog
## v1.0.1
[compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.0.0...module-solid-v1.0.1)
### 🩹 Fixes
- Add `target: esnext` by default ([#733](https://github.com/wxt-dev/wxt/pull/733))
### 🏡 Chore
- Add changelog ([21e8ca0](https://github.com/wxt-dev/wxt/commit/21e8ca0))
## v1.0.0
Initial release 🎉
+32
View File
@@ -0,0 +1,32 @@
# `@wxt-dev/module-solid`
Enables the use of [SolidJS](https://www.solidjs.com/) in your web extension, in HTML pages and content scripts.
This plugin makes a few changes:
1. Adds `vite-plugin-solid` to and sets `build.target: esnext` in the vite config
2. Adds the [`solid-js` preset](https://github.com/unjs/unimport/blob/main/src/presets/solid.ts) to auto-imports
## Usage
```sh
pnpm i solid-js
pnpm i -D @wxt-dev/module-solid
```
Then add the module to your config:
```ts
// wxt.config.ts
export default defineConfig({
// Required
modules: ['@wxt-dev/module-solid'],
// Optional: Pass options to the module:
solid: {
vite: {
// ...
},
},
});
```
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@wxt-dev/module-solid",
"version": "1.0.1",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": [
"dist"
],
"scripts": {
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
},
"peerDependencies": {
"wxt": ">=0.18.6"
},
"dependencies": {
"vite-plugin-solid": "^2.10.2"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
}
+30
View File
@@ -0,0 +1,30 @@
import 'wxt';
import { addImportPreset, addViteConfig, defineWxtModule } from 'wxt/modules';
import solid, { Options as PluginOptions } from 'vite-plugin-solid';
export default defineWxtModule<SolidModuleOptions>({
name: '@wxt-dev/module-solid',
configKey: 'solid',
setup(wxt, options) {
const { vite } = options ?? {};
addViteConfig(wxt, () => ({
plugins: [solid(vite)],
build: {
target: 'esnext',
},
}));
addImportPreset(wxt, 'solid-js');
},
});
export interface SolidModuleOptions {
vite?: PluginOptions;
}
declare module 'wxt' {
export interface InlineConfig {
solid?: SolidModuleOptions;
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"exclude": ["node_modules/**", "dist/**"]
}
+5
View File
@@ -0,0 +1,5 @@
# Changelog
## v1.0.0
Initial release 🎉
+32
View File
@@ -0,0 +1,32 @@
# `@wxt-dev/module-svelte`
Enables the use of [Svelte](https://svelte.dev/) in your web extension, in HTML pages and content scripts.
This plugin makes a few changes:
1. Adds `@sveltejs/vite-plugin-svelte` to vite
2. Adds the [`svelte` preset](https://github.com/unjs/unimport/blob/main/src/presets/vue.ts) to auto-imports
## Usage
```sh
pnpm i svelte
pnpm i -D @wxt-dev/module-svelte
```
Then add the module to your config:
```ts
// wxt.config.ts
export default defineConfig({
// Required
modules: ['@wxt-dev/module-svelte'],
// Optional: Pass options to the module:
svelte: {
vite: {
// ...
},
},
});
```
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@wxt-dev/module-svelte",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": [
"dist"
],
"scripts": {
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
},
"peerDependencies": {
"wxt": ">=0.18.6"
},
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.1"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
}
+38
View File
@@ -0,0 +1,38 @@
import 'wxt';
import { addImportPreset, addViteConfig, defineWxtModule } from 'wxt/modules';
import {
svelte,
vitePreprocess,
Options as PluginOptions,
} from '@sveltejs/vite-plugin-svelte';
export default defineWxtModule<SvelteModuleOptions>({
name: '@wxt-dev/module-svelte',
configKey: 'svelte',
setup(wxt, options) {
const { vite } = options ?? {};
addViteConfig(wxt, () => ({
plugins: [
svelte({
// Using a svelte.config.js file causes a segmentation fault when importing the file
configFile: false,
preprocess: [vitePreprocess()],
...vite,
}),
],
}));
addImportPreset(wxt, 'svelte');
},
});
export interface SvelteModuleOptions {
vite?: Partial<PluginOptions>;
}
declare module 'wxt' {
export interface InlineConfig {
svelte?: SvelteModuleOptions;
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"exclude": ["node_modules/**", "dist/**"]
}
+5
View File
@@ -0,0 +1,5 @@
# Changelog
## v1.0.0
Initial release 🎉
+36
View File
@@ -0,0 +1,36 @@
# `@wxt-dev/module-vue`
Enables the use of [Vue](https://vuejs.org/) in your web extension, in HTML pages and content scripts.
This plugin makes a few changes:
1. Adds `@vitejs/plugin-vue` to vite config
2. Adds the [`vue` preset](https://github.com/unjs/unimport/blob/main/src/presets/vue.ts) to auto-imports
3. Applies sourcemap fix to prevent HMR errors during development
4. Enable auto-imports in `.vue` files
## Usage
```sh
pnpm i vue
pnpm i -D @wxt-dev/module-vue
```
Then add the module to your config:
```ts
// wxt.config.ts
export default defineConfig({
// Required
modules: ['@wxt-dev/module-vue'],
// Optional: Pass options to the module:
vue: {
vite: {
script: {
propsDestructure: true,
},
},
},
});
```
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@wxt-dev/module-vue",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": [
"dist"
],
"scripts": {
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
},
"peerDependencies": {
"wxt": ">=0.18.6"
},
"dependencies": {
"@vitejs/plugin-vue": "^5.0.5"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
}
+47
View File
@@ -0,0 +1,47 @@
import 'wxt';
import { addImportPreset, addViteConfig, defineWxtModule } from 'wxt/modules';
import vue, { Options as ViteOptions } from '@vitejs/plugin-vue';
export default defineWxtModule<VueModuleOptions>({
name: '@wxt-dev/module-vue',
configKey: 'vue',
setup(wxt, options) {
const { vite } = options ?? {};
// Add plugin & set sourcemap option
addViteConfig(wxt, ({ command }) => ({
// @ts-ignore: Ignore vite version issues
plugins: [vue(vite)],
build: {
// Fixes known issue: https://github.com/wxt-dev/wxt/pull/607
sourcemap: false,
},
}));
// Enable auto-imports in template files
wxt.hooks.hook('ready', (wxt) => {
if (!wxt.config.imports) return;
wxt.config.imports.addons ??= {};
if (!Array.isArray(wxt.config.imports.addons)) {
wxt.config.imports.addons.vueTemplate = true;
} else {
wxt.logger.warn(
'Could not enable auto-imports in vue templates when using and array for imports.addons',
);
}
});
addImportPreset(wxt, 'vue');
},
});
export interface VueModuleOptions {
vite?: ViteOptions;
}
declare module 'wxt' {
export interface InlineConfig {
vue?: VueModuleOptions;
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"exclude": ["node_modules/**", "dist/**"]
}
+16 -14
View File
@@ -4,28 +4,30 @@
"private": true,
"type": "module",
"scripts": {
"dev": "pnpm build:deps && wxt",
"build:deps": "pnpm --filter wxt build",
"build": "pnpm build:deps && wxt build",
"build:all": "pnpm build:deps && run-s -s 'build:all:*'",
"dev": "buildc --deps-only -- wxt",
"build": "buildc -- wxt build",
"build:all": "buildc --deps-only -- run-s -s 'build:all:*'",
"build:all:chrome-mv3": "wxt build",
"build:all:chrome-mv2": "wxt build --mv2",
"build:all:firefox-mv3": "wxt build -b firefox --mv3",
"build:all:firefox-mv2": "wxt build -b firefox",
"test": "pnpm build:deps && vitest",
"zip": "pnpm build:deps && wxt zip",
"check": "pnpm build:deps && check",
"postinstall": "pnpm build:deps && wxt prepare"
"test": "buildc --deps-only -- vitest",
"zip": "buildc --deps-only -- wxt zip",
"check": "buildc --deps-only -- check",
"postinstall": "buildc --deps-only -- wxt prepare"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/react": "^18.2.34",
"@types/react-dom": "^18.2.14",
"sass": "^1.69.5",
"typescript": "^5.3.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"sass": "^1.77.5",
"typescript": "^5.4.5",
"wxt": "workspace:*"
},
"buildc": {
"outDir": ".output/chrome-mv3"
}
}
@@ -13,7 +13,7 @@ export default defineContentScript({
anchor: 'form[role=search]',
onMount: (container) => {
const app = document.createElement('div');
app.textContent = 'Custom content script UI';
app.textContent = browser.i18n.getMessage('prompt_for_name');
container.append(app);
},
});
-1
View File
@@ -6,6 +6,5 @@ export default defineProject({
mockReset: true,
restoreMocks: true,
},
// @ts-expect-error
plugins: [WxtVitest()],
});
+3
View File
@@ -21,6 +21,9 @@ export default defineConfig({
experimental: {
viteRuntime: true,
},
runner: {
startUrls: ['https://duckduckgo.com'],
},
example: {
a: 'a',
// @ts-expect-error: c is not defined, this should error out
+53 -1
View File
@@ -1,8 +1,60 @@
# Changelog
## v0.18.8
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.7...wxt-v0.18.8)
### 🚀 Enhancements
- **dev:** Reload extension when public files change ([#752](https://github.com/wxt-dev/wxt/pull/752))
### 🩹 Fixes
- Don't load plugins twice in HTML pages ([#746](https://github.com/wxt-dev/wxt/pull/746))
- Ignore .wxt file changes in dev ([#755](https://github.com/wxt-dev/wxt/pull/755))
- **modules:** `addViteConfig` ignored user vite config ([#760](https://github.com/wxt-dev/wxt/pull/760))
### 🏡 Chore
- Refactor client web socket util ([#753](https://github.com/wxt-dev/wxt/pull/753))
- Add E2E test for `addImportPreset` ([9fc6408](https://github.com/wxt-dev/wxt/commit/9fc6408))
## v0.18.7
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.6...wxt-v0.18.7)
### 🩹 Fixes
- **dev:** Fix CSP error loading HTML plugins ([#723](https://github.com/wxt-dev/wxt/pull/723))
- Generalize react-refresh preamble logic to virtualize all inline scripts ([#728](https://github.com/wxt-dev/wxt/pull/728))
- **zip:** Revert dotfile changes from #674 ([#742](https://github.com/wxt-dev/wxt/pull/742), [#674](https://github.com/wxt-dev/wxt/issues/674))
### 🏡 Chore
- **deps:** Upgrade and sync all dependencies ([#725](https://github.com/wxt-dev/wxt/pull/725))
- Extract build cache script to NPM package ([#737](https://github.com/wxt-dev/wxt/pull/737))
## v0.18.6
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.5...wxt-v0.18.6)
### 🚀 Enhancements
- **modules:** Add new `addImportPreset` helper function ([#720](https://github.com/wxt-dev/wxt/pull/720))
### 🩹 Fixes
- **types:** Module `options` param is optional, but types did not reflect that ([#719](https://github.com/wxt-dev/wxt/pull/719))
- **modules:** Re-export `WxtModule` type from `wxt/modules` to prevent TS2742 ([#721](https://github.com/wxt-dev/wxt/pull/721))
- **modules:** Add modules to TS project so type augmentation works ([#722](https://github.com/wxt-dev/wxt/pull/722))
### 🏡 Chore
- **dev-deps:** Upgrade typescript to 5.4 ([#718](https://github.com/wxt-dev/wxt/pull/718))
## v0.18.5
[compare changes](https://github.com/wxt-dev/wxt/compare/0.18.4...0.18.5)
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.4...wxt-v0.18.5)
### 🚀 Enhancements
+24
View File
@@ -287,5 +287,29 @@ describe('Module Helpers', () => {
await expect(project.serializeOutput()).resolves.toContain(expectedText);
});
it('should add preset', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/background.ts',
`export default defineBackground(() => {
customImport();
});`,
);
project.addFile(
'modules/test.ts',
`import { defineWxtModule, addImportPreset } from 'wxt/modules';
export default defineWxtModule((wxt) => {
addImportPreset(wxt, "vue");
})`,
);
await project.build();
await expect(
project.serializeFile('.wxt/types/imports.d.ts'),
).resolves.toContain("const ref: typeof import('vue')['ref']");
});
});
});
@@ -280,8 +280,7 @@ describe('Output Directory Structure', () => {
----------------------------------------
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 (typeof arg === "function") return { main: arg };
return arg;
}
const definition = defineBackground({
@@ -363,8 +362,7 @@ describe('Output Directory Structure', () => {
var _background = function() {
"use strict";
function defineBackground(arg) {
if (typeof arg === "function")
return { main: arg };
if (typeof arg === "function") return { main: arg };
return arg;
}
function logHello(name) {
@@ -375,4 +375,9 @@ describe('TypeScript Project', () => {
}"
`);
});
// 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',
);
});
+4 -2
View File
@@ -97,7 +97,8 @@ describe('Zipping', () => {
expect(await project.fileExists(sourcesZip)).toBe(true);
});
it('should not zip hidden files into sources by default', async () => {
// TODO: Fix #738 and re-enable
it.skip('should not zip hidden files into sources by default', async () => {
const project = new TestProject({
name: 'test',
version: '1.0.0',
@@ -117,7 +118,8 @@ describe('Zipping', () => {
expect(await project.fileExists(unzipDir, '.env')).toBe(false);
});
it('should allow zipping hidden files into sources when explicitly listed', async () => {
// TODO: Fix #738 and re-enable
it.skip('should allow zipping hidden files into sources when explicitly listed', async () => {
const project = new TestProject({
name: 'test',
version: '1.0.0',
+34 -33
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.18.5",
"version": "0.18.8",
"description": "Next gen framework for developing web extensions",
"repository": {
"type": "git",
@@ -92,71 +92,72 @@
},
"scripts": {
"wxt": "tsx src/cli/index.ts",
"build": "tsx scripts/build.ts",
"check": "run-s -c check:*",
"build": "buildc -- tsx scripts/build.ts",
"check": "buildc --deps-only -- run-s -c check:*",
"check:default": "check",
"check:tsc-virtual": "tsc --noEmit -p src/virtual",
"test": "vitest",
"test:e2e": "vitest -r e2e",
"test": "buildc --deps-only -- vitest",
"test:e2e": "buildc --deps-only -- vitest -r e2e",
"sync-releases": "pnpx changelogen@latest gh release"
},
"dependencies": {
"@aklinker1/rollup-plugin-visualizer": "5.12.0",
"@types/webextension-polyfill": "^0.10.5",
"@types/webextension-polyfill": "^0.10.7",
"@webext-core/fake-browser": "^1.3.1",
"@webext-core/isolated-element": "^1.1.2",
"@webext-core/match-patterns": "^1.0.3",
"async-mutex": "^0.4.0",
"c12": "^1.5.1",
"async-mutex": "^0.4.1",
"c12": "^1.11.1",
"cac": "^6.7.14",
"chokidar": "^3.5.3",
"chokidar": "^3.6.0",
"ci-info": "^4.0.0",
"defu": "^6.1.3",
"defu": "^6.1.4",
"dequal": "^2.0.3",
"esbuild": "^0.19.5",
"fast-glob": "^3.3.1",
"filesize": "^10.0.8",
"fs-extra": "^11.1.1",
"get-port": "^7.0.0",
"giget": "^1.1.3",
"esbuild": "^0.19.12",
"fast-glob": "^3.3.2",
"filesize": "^10.1.2",
"fs-extra": "^11.2.0",
"get-port": "^7.1.0",
"giget": "^1.2.3",
"hookable": "^5.5.3",
"is-wsl": "^3.0.0",
"jiti": "^1.21.0",
"is-wsl": "^3.1.0",
"jiti": "^1.21.6",
"json5": "^2.2.3",
"jszip": "^3.10.1",
"linkedom": "^0.18.2",
"linkedom": "^0.18.3",
"magicast": "^0.3.4",
"minimatch": "^9.0.3",
"minimatch": "^9.0.4",
"natural-compare": "^1.4.0",
"normalize-path": "^3.0.0",
"nypm": "^0.3.8",
"ohash": "^1.1.3",
"open": "^10.1.0",
"ora": "^7.0.1",
"picocolors": "^1.0.0",
"picocolors": "^1.0.1",
"prompts": "^2.4.2",
"publish-browser-extension": "^2.1.3",
"unimport": "^3.4.0",
"vite": "^5.2.8",
"unimport": "^3.7.2",
"vite": "^5.3.0",
"web-ext-run": "^0.2.0",
"webextension-polyfill": "^0.10.0"
},
"devDependencies": {
"@aklinker1/check": "^1.1.1",
"@faker-js/faker": "^8.3.1",
"@aklinker1/check": "^1.3.1",
"@faker-js/faker": "^8.4.1",
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
"@types/natural-compare": "^1.4.3",
"@types/node": "^20.10.3",
"@types/node": "^20.14.2",
"@types/normalize-path": "^3.0.2",
"@types/prompts": "^2.4.9",
"execa": "^9.1.0",
"execa": "^9.2.0",
"extract-zip": "^2.0.1",
"happy-dom": "^13.3.8",
"happy-dom": "^13.10.1",
"lodash.merge": "^4.6.2",
"p-map": "^7.0.0",
"publint": "^0.2.6",
"tsup": "^8.0.1",
"tsx": "^4.11.2",
"typescript": "^5.3.2"
"p-map": "^7.0.2",
"publint": "^0.2.8",
"tsup": "^8.1.0",
"tsx": "^4.15.4",
"typescript": "^5.4.5"
}
}
+50 -4
View File
@@ -1,5 +1,5 @@
import { fakeWxt } from '~/core/utils/testing/fake-objects';
import { addViteConfig } from '../modules';
import { addImportPreset, addViteConfig } from '../modules';
import { describe, it, expect } from 'vitest';
import { createHooks } from 'hookable';
@@ -13,10 +13,10 @@ describe('Module Utilities', () => {
const userConfig = {};
const moduleConfig = { build: { sourcemap: true } };
wxt.config.vite = () => userConfig;
wxt.config.vite = () => Promise.resolve(userConfig);
addViteConfig(wxt, () => moduleConfig);
await wxt.hooks.callHook('ready', wxt);
const actual: any = wxt.config.vite(wxt.config.env);
const actual = await wxt.config.vite(wxt.config.env);
expect(actual).toEqual(expected);
});
@@ -32,9 +32,55 @@ describe('Module Utilities', () => {
wxt.config.vite = () => userConfig;
addViteConfig(wxt, () => moduleConfig);
await wxt.hooks.callHook('ready', wxt);
const actual: any = wxt.config.vite(wxt.config.env);
const actual = await wxt.config.vite(wxt.config.env);
expect(actual).toEqual(expected);
});
});
describe('addImportPreset', () => {
it('should add the import to the config', async () => {
const preset = 'vue';
const wxt = fakeWxt({ hooks: createHooks() });
addImportPreset(wxt, preset);
await wxt.hooks.callHook('ready', wxt);
expect(wxt.config.imports && wxt.config.imports.presets).toContain(
preset,
);
});
it('should not add duplicate presets', async () => {
const preset = 'vue';
const wxt = fakeWxt({
hooks: createHooks(),
config: {
imports: {
presets: ['vue', 'react'],
},
},
});
addImportPreset(wxt, preset);
await wxt.hooks.callHook('ready', wxt);
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('ready', wxt);
expect(wxt.config.imports).toBe(false);
});
});
});
@@ -4,9 +4,10 @@ import { getEntrypointName } from '~/core/utils/entrypoints';
import { parseHTML } from 'linkedom';
import { dirname, relative, resolve } from 'node:path';
import { normalizePath } from '~/core/utils/paths';
import { murmurHash } from 'ohash';
// Cache the preamble script for all devHtmlPrerender plugins, not just one
let reactRefreshPreamble = '';
// Stored outside the plugin to effect all instances of the devHtmlPrerender plugin.
const inlineScriptContents: Record<number, string> = {};
/**
* Pre-renders the HTML entrypoints when building the extension to connect to the dev server.
@@ -20,8 +21,9 @@ export function devHtmlPrerender(
config.wxtModuleDir,
'dist/virtual/reload-html.js',
);
const virtualReactRefreshId = '@wxt/virtual-react-refresh';
const resolvedVirtualReactRefreshId = '\0' + virtualReactRefreshId;
const virtualInlineScript = 'virtual:wxt-inline-script';
const resolvedVirtualInlineScript = '\0' + virtualInlineScript;
return [
{
@@ -76,22 +78,22 @@ export function devHtmlPrerender(
const serverHtml = await server.transformHtml(url, html, originalUrl);
const { document } = parseHTML(serverHtml);
// React pages include a preamble as an unsafe-inline type="module" script to enable fast refresh, as shown here:
// https://github.com/wxt-dev/wxt/issues/157#issuecomment-1756497616
// Since unsafe-inline scripts are blocked by MV3 CSPs, we need to virtualize it.
const reactRefreshScript = Array.from(
document.querySelectorAll('script[type=module]'),
).find((script) => script.innerHTML.includes('@react-refresh'));
if (reactRefreshScript) {
// Save preamble to serve from server
reactRefreshPreamble = reactRefreshScript.innerHTML;
// Replace inline script with virtual module served via dev server.
// Extension CSP blocks inline scripts, so that's why we're pulling them
// out.
const inlineScripts = document.querySelectorAll('script:not([src])');
inlineScripts.forEach((script) => {
// Save the text content for later
const textContent = script.textContent ?? '';
const hash = murmurHash(textContent);
inlineScriptContents[hash] = textContent;
// Replace unsafe inline script
const virtualScript = document.createElement('script');
virtualScript.type = 'module';
virtualScript.src = `${server.origin}/${virtualReactRefreshId}`;
reactRefreshScript.replaceWith(virtualScript);
}
virtualScript.src = `${server.origin}/@id/${virtualInlineScript}?${hash}`;
script.replaceWith(virtualScript);
});
// Change /@vite/client -> http://localhost:3000/@vite/client
const viteClientScript = document.querySelector<HTMLScriptElement>(
@@ -112,18 +114,25 @@ export function devHtmlPrerender(
name: 'wxt:virtualize-react-refresh',
apply: 'serve',
resolveId(id) {
if (id === `/${virtualReactRefreshId}`) {
return resolvedVirtualReactRefreshId;
// Resolve inline scripts
if (id.startsWith(virtualInlineScript)) {
return '\0' + id;
}
// Ignore chunk contents when pre-rendering
// Ignore chunks during HTML file pre-rendering
if (id.startsWith('/chunks/')) {
return '\0noop';
}
},
load(id) {
if (id === resolvedVirtualReactRefreshId) {
return reactRefreshPreamble;
// 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];
}
// Ignore chunks during HTML file pre-rendering
if (id === '\0noop') {
return '';
}
@@ -46,10 +46,19 @@ try {
// Use "pre" so the new script is added before vite bundles all the scripts
order: 'pre',
handler(html, _ctx) {
const src =
config.command === 'serve'
? `http://${config.dev.server?.hostname}:${config.dev.server?.port}/@id/${virtualHtmlModuleId}`
: virtualHtmlModuleId;
const { document } = parseHTML(html);
const existing = document.querySelector(`script[src='${src}']`);
if (existing) return;
const script = document.createElement('script');
script.type = 'module';
script.src = 'virtual:wxt-html-plugins';
script.src = src;
if (document.head == null) {
const newHead = document.createElement('head');
document.documentElement.prepend(newHead);
+1
View File
@@ -153,6 +153,7 @@ function createFileReloader(server: WxtDevServer) {
// Here, "path" is a non-normalized path (ie: C:\\users\\... instead of C:/users/...)
if (path.startsWith(wxt.config.outBaseDir)) return;
if (path.startsWith(wxt.config.wxtDir)) return;
changeQueue.push([event, path]);
await fileChangedMutex.runExclusive(async () => {
@@ -1,6 +1,6 @@
{
"name": "yarn-ls",
"packageManager": "yarn@1.22.19",
"packageManager": "yarn@1.22.22",
"dependencies": {
"mime-types": "2.1.35"
},
@@ -100,6 +100,11 @@ describe('Detect Dev Changes', () => {
describe('Public Assets', () => {
it("should return 'extension-reload' without any groups to rebuild when the changed file is a public asset", () => {
const changes = ['/root/src/public/image.svg'];
setFakeWxt({
config: {
publicDir: '/root/src/public',
},
});
const asset1 = fakeOutputAsset({
fileName: 'image.svg',
});
@@ -114,10 +119,7 @@ describe('Detect Dev Changes', () => {
const expected: DevModeChange = {
type: 'extension-reload',
rebuildGroups: [],
cachedOutput: {
...currentOutput,
publicAssets: [asset2],
},
cachedOutput: currentOutput,
};
const actual = detectDevChanges(changes, currentOutput);
@@ -2,7 +2,6 @@ import {
BuildOutput,
BuildStepOutput,
EntrypointGroup,
OutputAsset,
OutputFile,
} from '~/types';
import { every, some } from '~/core/utils/arrays';
@@ -50,12 +49,25 @@ export function detectDevChanges(
findEffectedSteps(changedFile, currentOutput),
),
);
if (changedSteps.size === 0) return { type: 'no-change' };
if (changedSteps.size === 0) {
const hasPublicChange = some(changedFiles, (file) =>
file.startsWith(wxt.config.publicDir),
);
if (hasPublicChange) {
return {
type: 'extension-reload',
rebuildGroups: [],
cachedOutput: currentOutput,
};
} else {
return { type: 'no-change' };
}
}
const unchangedOutput: BuildOutput = {
manifest: currentOutput.manifest,
steps: [],
publicAssets: [],
publicAssets: [...currentOutput.publicAssets],
};
const changedOutput: BuildOutput = {
manifest: currentOutput.manifest,
@@ -70,13 +82,6 @@ export function detectDevChanges(
unchangedOutput.steps.push(step);
}
}
for (const asset of currentOutput.publicAssets) {
if (changedSteps.has(asset)) {
changedOutput.publicAssets.push(asset);
} else {
unchangedOutput.publicAssets.push(asset);
}
}
const isOnlyHtmlChanges =
changedFiles.length > 0 &&
@@ -117,8 +122,8 @@ export function detectDevChanges(
function findEffectedSteps(
changedFile: string,
currentOutput: BuildOutput,
): DetectedChange[] {
const changes: DetectedChange[] = [];
): BuildStepOutput[] {
const changes: BuildStepOutput[] = [];
const changedPath = normalizePath(changedFile);
const isChunkEffected = (chunk: OutputFile): boolean =>
@@ -135,11 +140,6 @@ function findEffectedSteps(
if (effectedChunk) changes.push(step);
}
const effectedAsset = currentOutput.publicAssets.find((chunk) =>
isChunkEffected(chunk),
);
if (effectedAsset) changes.push(effectedAsset);
return changes;
}
@@ -194,10 +194,3 @@ interface ContentScriptReload extends RebuildChange {
type: 'content-script-reload';
changedSteps: BuildStepOutput[];
}
/**
* When figuring out what needs reloaded, this stores the step that was changed, or the public
* directory asset that was changed. It doesn't know what type of change is required yet. Just an
* intermediate type.
*/
type DetectedChange = BuildStepOutput | OutputAsset;
@@ -204,6 +204,16 @@ async function writeMainDeclarationFile(references: string[]): Promise<string> {
(ref) =>
`/// <reference types="./${normalizePath(relative(dir, ref))}" />`,
),
// Add references to modules installed from NPM to the TS project so
// their type augmentation can update InlineConfig correctly. Local
// modules defined in <root>/modules are already apart of the project, so
// we don't need to add them.
...wxt.config.modules
.filter(
(module) => module.type === 'node_module' && module.configKey != null,
)
.map((module) => `/// <reference types="${module.id}" />`),
].join('\n') + '\n',
);
return filePath;
@@ -11,6 +11,7 @@ import {
Logger,
WxtCommand,
WxtModule,
WxtModuleWithMetadata,
} from '~/types';
import path from 'node:path';
import { createFsCache } from '~/core/utils/cache';
@@ -380,15 +381,21 @@ export async function mergeBuilderConfig(
export async function resolveWxtModules(
modulesDir: string,
modules: string[] = [],
): Promise<WxtModule<any>[]> {
// Resolve NPM packages
const npmModules = await Promise.all(
): Promise<WxtModuleWithMetadata<any>[]> {
// Resolve node_modules modules
const npmModules = await Promise.all<WxtModuleWithMetadata<any>>(
modules.map(async (moduleId) => {
const mod = await import(/* @vite-ignore */ moduleId);
const mod: { default: WxtModule<any> } = await import(
/* @vite-ignore */ moduleId
);
if (mod.default == null) {
throw Error('Module missing default export: ' + moduleId);
}
return mod.default;
return {
...mod.default,
type: 'node_module',
id: moduleId,
};
}),
);
@@ -397,10 +404,11 @@ export async function resolveWxtModules(
cwd: modulesDir,
onlyFiles: true,
}).catch(() => []);
const localModules = await Promise.all(
const localModules = await Promise.all<WxtModuleWithMetadata<any>>(
localModulePaths.map(async (file) => {
const absolutePath = normalizePath(path.resolve(modulesDir, file));
const { config } = await loadConfig<WxtModule<any>>({
configFile: path.resolve(modulesDir, file),
configFile: absolutePath,
globalRc: false,
rcFile: false,
packageJson: false,
@@ -413,10 +421,12 @@ export async function resolveWxtModules(
);
// Add name based on filename
config.name ??= file;
return config;
return {
...config,
type: 'local',
id: absolutePath,
};
}),
);
// Execute modules
return [...npmModules, ...localModules];
}
+2 -1
View File
@@ -104,7 +104,8 @@ async function zipDir(
// Ignore node_modules, otherwise this glob step takes forever
ignore: ['**/node_modules'],
onlyFiles: true,
dot: true,
// TODO: Fix #738
// dot: true,
})
).filter((relativePath) => {
return (
+52 -8
View File
@@ -13,6 +13,10 @@ import type {
import * as vite from 'vite';
import glob from 'fast-glob';
import { resolve } from 'node:path';
import type { UnimportOptions } from 'unimport';
// Re-export to prevent TS2742 type errors
export { WxtModule };
export function defineWxtModule<TOptions extends WxtModuleOptions>(
module: WxtModule<TOptions> | WxtModuleSetup<TOptions>,
@@ -100,13 +104,11 @@ export function addViteConfig(
): void {
wxt.hooks.hook('ready', (wxt) => {
const userVite = wxt.config.vite;
wxt.config.vite = (env) =>
vite.mergeConfig(
// Use config added by module as base
viteConfig(env) ?? {},
// Overwrite module config with user config
userVite(env),
);
wxt.config.vite = async (env) => {
const fromUser = await userVite(env);
const fromModule = viteConfig(env) ?? {};
return vite.mergeConfig(fromModule, fromUser);
};
});
}
@@ -123,8 +125,50 @@ export function addViteConfig(
* addWxtPlugin(wxt, "wxt-module-analytics/client-plugin");
* });
*/
export function addWxtPlugin(wxt: Wxt, plugin: string) {
export function addWxtPlugin(wxt: Wxt, plugin: string): void {
wxt.hooks.hook('ready', (wxt) => {
wxt.config.plugins.push(plugin);
});
}
/**
* Add an Unimport preset ([built-in](https://github.com/unjs/unimport?tab=readme-ov-file#built-in-presets),
* [custom](https://github.com/unjs/unimport?tab=readme-ov-file#custom-presets),
* or [auto-scanned](https://github.com/unjs/unimport?tab=readme-ov-file#exports-auto-scan)),
* to the project's list of auto-imported utilities.
*
* Some things to note:
* - This function will only de-duplicate built-in preset names. It will not
* stop you adding duplicate custom or auto-scanned presets.
* - If the project has disabled imports, this function has no effect.
*
* @param wxt The wxt instance provided by the module's setup function.
* @param preset The preset to add to the project.
*
* @example
* export default defineWxtModule((wxt) => {
* // Built-in preset:
* addImportPreset(wxt, "vue");
* // Custom preset:
* addImportPreset(wxt, {
* from: "vue",
* imports: ["ref", "reactive", ...],
* });
* // Auto-scanned preset:
* addImportPreset(wxt, { package: "vue" });
* });
*/
export function addImportPreset(
wxt: Wxt,
preset: UnimportOptions['presets'][0],
): void {
wxt.hooks.hook('ready', (wxt) => {
if (!wxt.config.imports) return;
wxt.config.imports.presets ??= [];
// De-dupelicate built-in named presets
if (wxt.config.imports.presets.includes(preset)) return;
wxt.config.imports.presets.push(preset);
});
}
@@ -0,0 +1,85 @@
import { logger } from './utils/logger';
interface WebSocketMessage {
type: string;
event: string;
data?: any;
}
export interface WxtWebSocket extends WebSocket {
addWxtEventListener(
type: 'wxt:reload-extension',
callback: (event: CustomEvent<undefined>) => void,
options?: AddEventListenerOptions | boolean,
): void;
addWxtEventListener(
type: 'wxt:reload-content-script',
callback?: (event: CustomEvent<ReloadContentScriptPayload>) => void,
options?: AddEventListenerOptions | boolean,
): void;
addWxtEventListener(
type: 'wxt:reload-page',
callback?: (event: CustomEvent<string>) => void,
options?: AddEventListenerOptions | boolean,
): void;
sendCustom(event: string, payload?: any): void;
}
let ws: WxtWebSocket | undefined;
/**
* Connect to the websocket and listen for messages.
*
* @param onMessage Optional callback that is called when a message is recieved and we've verified
* it's structure is what we expect.
*/
export function getDevServerWebSocket(): WxtWebSocket {
if (import.meta.env.COMMAND !== 'serve')
throw Error(
'Must be running WXT dev command to connect to call getDevServerWebSocket()',
);
if (ws == null) {
const serverUrl = `${__DEV_SERVER_PROTOCOL__}//${__DEV_SERVER_HOSTNAME__}:${__DEV_SERVER_PORT__}`;
logger.debug('Connecting to dev server @', serverUrl);
ws = new WebSocket(serverUrl, 'vite-hmr') as WxtWebSocket;
ws.addWxtEventListener = ws.addEventListener.bind(ws);
ws.sendCustom = (event, payload) =>
ws?.send(JSON.stringify({ type: 'custom', event, payload }));
ws.addEventListener('open', () => {
logger.debug('Connected to dev server');
});
ws.addEventListener('close', () => {
logger.debug('Disconnected from dev server');
});
ws.addEventListener('error', (event) => {
logger.error('Failed to connect to dev server', event);
});
ws.addEventListener('message', (e) => {
try {
const message = JSON.parse(e.data) as WebSocketMessage;
if (message.type === 'custom') {
ws?.dispatchEvent(
new CustomEvent(message.event, { detail: message.data }),
);
}
} catch (err) {
logger.error('Failed to handle message', err);
}
});
}
return ws;
}
export interface ReloadContentScriptPayload {
registration?: 'manifest' | 'runtime';
contentScript: {
matches: string[];
js?: string[];
css?: string[];
};
}
+8 -2
View File
@@ -1185,7 +1185,7 @@ export interface ResolvedConfig {
reloadCommand: string | false;
};
hooks: NestedHooks<WxtHooks>;
modules: WxtModule<any>[];
modules: WxtModuleWithMetadata<any>[];
/**
* An array of string to import plugins from. These paths should be
* resolvable by vite, and they should `export default defineWxtPlugin(...)`.
@@ -1300,7 +1300,7 @@ export type WxtModuleOptions = Record<string, any>;
export type WxtModuleSetup<TOptions extends WxtModuleOptions> = (
wxt: Wxt,
moduleOptions: TOptions,
moduleOptions?: TOptions,
) => void | Promise<void>;
export interface WxtModule<TOptions extends WxtModuleOptions> {
@@ -1325,6 +1325,12 @@ export interface WxtModule<TOptions extends WxtModuleOptions> {
setup?: WxtModuleSetup<TOptions>;
}
export interface WxtModuleWithMetadata<TOptions extends WxtModuleOptions>
extends WxtModule<TOptions> {
type: 'local' | 'node_module';
id: string;
}
export interface ResolvedPublicFile {
/**
* The absolute path to the file that will be copied to the output directory.
@@ -1,6 +1,6 @@
import definition from 'virtual:user-background-entrypoint';
import { initPlugins } from 'virtual:wxt-plugins';
import { setupWebSocket } from './utils/setup-web-socket';
import { getDevServerWebSocket } from '../sandbox/dev-server-websocket';
import { logger } from '../sandbox/utils/logger';
import { browser } from 'wxt/browser';
import { keepServiceWorkerAlive } from './utils/keep-service-worker-alive';
@@ -8,18 +8,19 @@ import { reloadContentScript } from './utils/reload-content-scripts';
if (import.meta.env.COMMAND === 'serve') {
try {
const ws = setupWebSocket((message) => {
if (message.event === 'wxt:reload-extension') browser.runtime.reload();
if (message.event === 'wxt:reload-content-script' && message.data != null)
reloadContentScript(message.data);
const ws = getDevServerWebSocket();
ws.addWxtEventListener('wxt:reload-extension', () => {
browser.runtime.reload();
});
ws.addWxtEventListener('wxt:reload-content-script', (event) => {
reloadContentScript(event.detail);
});
if (import.meta.env.MANIFEST_VERSION === 3) {
// Tell the server the background script is loaded and ready to go
ws.addEventListener('open', () => {
const msg = { type: 'custom', event: 'wxt:background-initialized' };
ws.send(JSON.stringify(msg));
});
ws.addEventListener('open', () =>
ws.sendCustom('wxt:background-initialized'),
);
// Web Socket will disconnect if the service worker is killed
keepServiceWorkerAlive();
+5 -9
View File
@@ -1,16 +1,12 @@
import { logger } from '../sandbox/utils/logger';
import { setupWebSocket } from './utils/setup-web-socket';
import { getDevServerWebSocket } from '../sandbox/dev-server-websocket';
if (import.meta.env.COMMAND === 'serve') {
try {
setupWebSocket((message) => {
if (message.event === 'wxt:reload-page') {
// We need to remove the initial slash from the path to compare correctly
// "popup.html" === "/popup.html".substring(1)
if (message.data === location.pathname.substring(1)) {
location.reload();
}
}
const ws = getDevServerWebSocket();
ws.addWxtEventListener('wxt:reload-page', (event) => {
// "popup.html" === "/popup.html".substring(1)
if (event.detail === location.pathname.substring(1)) location.reload();
});
} catch (err) {
logger.error('Failed to setup web socket connection with dev server', err);
@@ -1,16 +1,7 @@
import { browser } from 'wxt/browser';
import { logger } from '../../sandbox/utils/logger';
import { MatchPattern } from 'wxt/sandbox';
interface ContentScript {
matches: string[];
js?: string[];
css?: string[];
}
interface ReloadContentScriptPayload {
registration?: 'manifest' | 'runtime';
contentScript: ContentScript;
}
import type { ReloadContentScriptPayload } from '../../sandbox/dev-server-websocket';
export function reloadContentScript(payload: ReloadContentScriptPayload) {
const manifest = browser.runtime.getManifest();
@@ -32,6 +23,8 @@ export async function reloadContentScriptMv3({
}
}
type ContentScript = ReloadContentScriptPayload['contentScript'];
export async function reloadManifestContentScriptMv3(
contentScript: ContentScript,
) {
@@ -1,44 +0,0 @@
import { logger } from '../../sandbox/utils/logger';
interface WebSocketMessage {
type: string;
event: string;
data?: any;
}
/**
* Connect to the websocket and listen for messages.
*
* @param onMessage Optional callback that is called when a message is recieved and we've verified
* it's structure is what we expect.
*/
export function setupWebSocket(
onMessage?: (message: WebSocketMessage) => void,
) {
const serverUrl = `${__DEV_SERVER_PROTOCOL__}//${__DEV_SERVER_HOSTNAME__}:${__DEV_SERVER_PORT__}`;
logger.debug('Connecting to dev server @', serverUrl);
const ws = new WebSocket(serverUrl, 'vite-hmr');
ws.addEventListener('open', () => {
logger.debug('Connected to dev server');
});
ws.addEventListener('close', () => {
logger.debug('Disconnected from dev server');
});
ws.addEventListener('error', (event) => {
logger.error('Failed to connect to dev server', event);
});
ws.addEventListener('message', (e) => {
try {
const message = JSON.parse(e.data) as WebSocketMessage;
if (message.type === 'custom' && message.event?.startsWith?.('wxt:')) {
onMessage?.(message);
}
} catch (err) {
logger.error('Failed to handle message', err);
}
});
return ws;
}
+2145 -856
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -40,13 +40,14 @@ await execa('pnpm', ['version', bumpType], {
});
const updatedPkgJson = await fs.readJson(pkgJsonPath);
const newVersion: string = updatedPkgJson.version;
const newTag = getPkgTag(pkg, newVersion);
consola.info('Bump:', { currentVersion, bumpType, newVersion });
// Generate changelog
const versionChangelog = await generateMarkDown(commits, {
...config,
from: currentVersion,
to: newVersion,
from: prevTag,
to: newTag,
});
const versionChangelogBody = versionChangelog
.split('\n')
@@ -80,5 +81,5 @@ await execa('git', [
'-m',
`chore(release): ${pkgName} v${newVersion}`,
]);
await execa('git', ['tag', getPkgTag(pkg, newVersion)]);
await execa('git', ['tag', newTag]);
consola.success('Committed version and changelog');
+2
View File
@@ -29,5 +29,7 @@ await createGithubRelease(config, {
tag_name: prevTag,
name: `${pkgName} v${currentVersion}`,
body: releases[0].body,
// @ts-expect-error: Not typed in changelogen, but present: https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release
make_latest: String(pkg === 'wxt'),
});
consola.success('Created release');
+12 -8
View File
@@ -27,14 +27,18 @@ config.tokens.github = process.env.GITHUB_TOKEN;
// Update releases
for (const release of releases) {
const tag = getPkgTag(pkg, release.version);
const existing = await getGithubReleaseByTag(config, tag);
if (existing.body !== release.body) {
await updateGithubRelease(config, existing.id!, {
tag_name: tag,
name: `${pkgName} v${release.version}`,
body: release.body,
});
try {
const existing = await getGithubReleaseByTag(config, tag);
if (existing.body !== release.body) {
await updateGithubRelease(config, existing.id!, {
tag_name: tag,
name: `${pkgName} v${release.version}`,
body: release.body,
});
}
consola.success(`Synced \`${tag}\``);
} catch (err) {
consola.fail(`\`${tag}\``, err);
}
consola.success(`Synced \`${tag}\``);
}
consola.success('Done');
+4
View File
@@ -0,0 +1,4 @@
// https://github.com/antfu-collective/taze?tab=readme-ov-file#config-file
export default {
exclude: ['typedoc', 'typedoc-plugin-markdown', 'typedoc-vitepress-theme'],
};
+7 -7
View File
@@ -15,14 +15,14 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/react": "^18.2.46",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"typescript": "^5.3.3",
"wxt": "^0.18.0"
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@wxt-dev/module-react": "^1.0.0",
"typescript": "^5.4.5",
"wxt": "^0.18.6"
}
}
+1 -4
View File
@@ -1,9 +1,6 @@
import { defineConfig } from 'wxt';
import react from '@vitejs/plugin-react';
// See https://wxt.dev/api/config.html
export default defineConfig({
vite: () => ({
plugins: [react()],
}),
modules: ['@wxt-dev/module-react'],
});
+4 -4
View File
@@ -15,11 +15,11 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"solid-js": "^1.8.7"
"solid-js": "^1.8.17"
},
"devDependencies": {
"typescript": "^5.3.3",
"vite-plugin-solid": "^2.8.0",
"wxt": "^0.18.0"
"typescript": "^5.4.5",
"@wxt-dev/module-solid": "^1.0.0",
"wxt": "^0.18.6"
}
}
+1 -7
View File
@@ -1,12 +1,6 @@
import { defineConfig } from 'wxt';
import Solid from 'vite-plugin-solid';
// See https://wxt.dev/api/config.html
export default defineConfig({
vite: () => ({
build: {
target: 'esnext',
},
plugins: [Solid()],
}),
modules: ['@wxt-dev/module-solid'],
});
+7 -7
View File
@@ -14,12 +14,12 @@
"postinstall": "wxt prepare"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@tsconfig/svelte": "^5.0.2",
"svelte": "^4.2.8",
"svelte-check": "^3.6.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"wxt": "^0.18.0"
"@tsconfig/svelte": "^5.0.4",
"@wxt-dev/module-svelte": "^1.0.0",
"svelte": "^4.2.18",
"svelte-check": "^3.8.0",
"tslib": "^2.6.3",
"typescript": "^5.4.5",
"wxt": "^0.18.6"
}
}
+1 -10
View File
@@ -1,16 +1,7 @@
import { defineConfig } from 'wxt';
import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte';
// See https://wxt.dev/api/config.html
export default defineConfig({
srcDir: 'src',
vite: () => ({
plugins: [
svelte({
// Using a svelte.config.js file causes a segmentation fault when importing the file
configFile: false,
preprocess: [vitePreprocess()],
}),
],
}),
modules: ['@wxt-dev/module-svelte'],
});
+2 -2
View File
@@ -15,7 +15,7 @@
"postinstall": "wxt prepare"
},
"devDependencies": {
"typescript": "^5.3.3",
"wxt": "^0.18.0"
"typescript": "^5.4.5",
"wxt": "^0.18.6"
}
}
+5 -5
View File
@@ -15,12 +15,12 @@
"postinstall": "wxt prepare"
},
"dependencies": {
"vue": "^3.4.0"
"vue": "^3.4.27"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.1",
"typescript": "^5.3.3",
"vue-tsc": "^2.0.6",
"wxt": "^0.18.0"
"@wxt-dev/module-vue": "^1.0.0",
"typescript": "^5.4.5",
"vue-tsc": "^2.0.21",
"wxt": "^0.18.6"
}
}
+1 -13
View File
@@ -1,18 +1,6 @@
import { defineConfig } from 'wxt';
import vue from '@vitejs/plugin-vue';
// See https://wxt.dev/api/config.html
export default defineConfig({
imports: {
addons: {
vueTemplate: true,
},
},
vite: () => ({
plugins: [vue()],
build: {
// Enabling sourcemaps with Vue during development is known to cause problems with Vue
sourcemap: false,
},
}),
modules: ['@wxt-dev/module-vue'],
});