Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16d8240406 | |||
| 24cd01dd31 | |||
| fb22beadb8 | |||
| fa85177648 | |||
| ed07a49dd2 | |||
| 09c9de0a60 | |||
| 523296b29a | |||
| ef67ef47aa | |||
| cb5eb68d62 | |||
| 2e9ab75e2b | |||
| 0c2d795800 | |||
| 97aa173f7e | |||
| 9eb64d94d6 | |||
| a0971cab52 | |||
| 127562e9d0 | |||
| 038e7450e0 | |||
| 6c00763187 | |||
| 2da0f3d686 |
@@ -12,6 +12,7 @@ on:
|
||||
- module-vue
|
||||
- module-svelte
|
||||
- module-solid
|
||||
- auto-icons
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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/', [
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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({
|
||||
module: ['@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).
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@wxt-dev/auto-icons",
|
||||
"version": "1.0.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
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(options, {
|
||||
enabled: true,
|
||||
baseIconsPath: resolve(wxt.config.srcDir, 'assets/icon.png'),
|
||||
grayscaleOnDevelopment: true,
|
||||
sizes: [128, 48, 32, 16],
|
||||
});
|
||||
|
||||
if (!parsedOptions.enabled)
|
||||
return wxt.logger.warn(`\`[auto-icons]\` ${this.name} disabled`);
|
||||
|
||||
if (!(await exists(parsedOptions.baseIconsPath))) {
|
||||
const relativePath = relative(process.cwd(), parsedOptions.baseIconsPath);
|
||||
return wxt.logger.warn(
|
||||
`\`[auto-icons]\` Skipping icon generation, no base icon found at ${relativePath}`,
|
||||
);
|
||||
}
|
||||
|
||||
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(parsedOptions.baseIconsPath).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.
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"exclude": ["node_modules/**", "dist/**"]
|
||||
}
|
||||
@@ -9,7 +9,6 @@ export default defineWxtModule<ReactModuleOptions>({
|
||||
const { vite } = options ?? {};
|
||||
|
||||
addViteConfig(wxt, () => ({
|
||||
// @ts-expect-error: Vite version conflicts
|
||||
plugins: [react(vite)],
|
||||
}));
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
"check": "buildc --deps-only -- check",
|
||||
"check": "pnpm build && check",
|
||||
"build": "buildc -- unbuild",
|
||||
"prepare": "buildc --deps-only -- wxt prepare"
|
||||
},
|
||||
@@ -39,7 +39,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:*"
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
"check": "buildc --deps-only -- check",
|
||||
"check": "pnpm build && check",
|
||||
"build": "buildc -- unbuild",
|
||||
"prepare": "buildc --deps-only -- wxt prepare"
|
||||
},
|
||||
@@ -35,8 +35,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:*"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "buildc -- unbuild",
|
||||
"check": "buildc --deps-only -- check"
|
||||
"check": "pnpm build && check"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"wxt": ">=0.18.6"
|
||||
@@ -33,7 +33,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:*"
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -22,18 +22,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:*"
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -6,8 +6,5 @@ export default defineProject({
|
||||
mockReset: true,
|
||||
restoreMocks: true,
|
||||
},
|
||||
plugins: [
|
||||
// @ts-expect-error: Vite version mismatch
|
||||
WxtVitest(),
|
||||
],
|
||||
plugins: [WxtVitest()],
|
||||
});
|
||||
|
||||
@@ -1,8 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
## 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) • [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.15...wxt-v0.19.0)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
@@ -36,11 +73,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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.19.0",
|
||||
"version": "0.19.2",
|
||||
"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",
|
||||
@@ -116,20 +116,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 +139,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": {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Generated
+22
-15
@@ -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.
|
||||
@@ -72,47 +73,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) {
|
||||
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
|
||||
Generated
+5403
-4087
File diff suppressed because it is too large
Load Diff
@@ -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) • [compare changes]`,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"postinstall": "wxt prepare"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.5.3",
|
||||
"wxt": "^0.18.15"
|
||||
"typescript": "^5.5.4",
|
||||
"wxt": "^0.19.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user