Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca15b1f321 | |||
| 448436cb08 | |||
| c9ddc3d0f2 | |||
| 6b72f883b6 | |||
| cd3446bd7f | |||
| 829b371bba | |||
| 36b399dd49 | |||
| 08f32aa685 | |||
| 05db264e02 | |||
| de5a01834c | |||
| 1d260dbacb | |||
| 53c63b4556 | |||
| 816878bc15 | |||
| e087a0d6ef | |||
| 56b71491a6 | |||
| 121b521157 | |||
| 16c2e3218b | |||
| d9cba55d34 | |||
| 832cd5d640 | |||
| bb95e08d71 | |||
| 35269dad9e | |||
| 64d1498460 | |||
| 0d540a6df9 | |||
| b47c150daa |
@@ -1,5 +1,69 @@
|
||||
# Changelog
|
||||
|
||||
## v0.17.9
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.8...v0.17.9)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Add `{{mode}}` Template Variable ([#566](https://github.com/wxt-dev/wxt/pull/566))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Don't override `wxt.config.ts` options when CLI flags are not passed ([#567](https://github.com/wxt-dev/wxt/pull/567))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Merge user config using `defu` ([#568](https://github.com/wxt-dev/wxt/pull/568))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Guillaume ([@GuiEpi](http://github.com/GuiEpi))
|
||||
|
||||
## v0.17.8
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.7...v0.17.8)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- **analysis:** Open `stats.html` file automatically ([#564](https://github.com/wxt-dev/wxt/pull/564))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- **init:** Better error logging when templates fail to load ([b47c150](https://github.com/wxt-dev/wxt/commit/b47c150))
|
||||
- Remove deprecated extension from Vue template ([#534](https://github.com/wxt-dev/wxt/pull/534))
|
||||
- Append option description error ([#546](https://github.com/wxt-dev/wxt/pull/546))
|
||||
- **init:** Don't overwrite existing files when initializing a new project ([#556](https://github.com/wxt-dev/wxt/pull/556))
|
||||
- **dev:** Don't crash dev mode when rebuild fails ([#565](https://github.com/wxt-dev/wxt/pull/565))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Replace `pnpx` with `pnpm dlx` ([#527](https://github.com/wxt-dev/wxt/pull/527))
|
||||
- Update homepage demo video ([35269da](https://github.com/wxt-dev/wxt/commit/35269da))
|
||||
- Update README demo video ([#539](https://github.com/wxt-dev/wxt/pull/539))
|
||||
- Add Plex skipper to "Using WXT" section ([#541](https://github.com/wxt-dev/wxt/pull/541))
|
||||
- Add documentation for Bun.sh ([#543](https://github.com/wxt-dev/wxt/pull/543))
|
||||
- Mention adding unlisted scripts and pages to `web_accessible_resources` ([121b521](https://github.com/wxt-dev/wxt/commit/121b521))
|
||||
- Add examples for GitHub Actions ([#540](https://github.com/wxt-dev/wxt/pull/540))
|
||||
- Fixed "Reload the Extension" section ([#559](https://github.com/wxt-dev/wxt/pull/559))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Increase unit test timeout ([d9cba55](https://github.com/wxt-dev/wxt/commit/d9cba55))
|
||||
- Increase hook timeout for Windows/NPM tests ([56b7149](https://github.com/wxt-dev/wxt/commit/56b7149))
|
||||
- Switch to seed plugin for testing ([#547](https://github.com/wxt-dev/wxt/pull/547))
|
||||
- **vue-template:** Upgrade to `vue-tsc` v2 ([#549](https://github.com/wxt-dev/wxt/pull/549))
|
||||
- Add a test covering `wxt init` in a non-empty directory ([#563](https://github.com/wxt-dev/wxt/pull/563))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Btea ([@btea](http://github.com/btea))
|
||||
- Vlad Fedosov ([@StyleT](http://github.com/StyleT))
|
||||
- Lpmvb ([@Lpmvb](http://github.com/Lpmvb))
|
||||
- Guillaume ([@GuiEpi](http://github.com/GuiEpi))
|
||||
- Sunshio ([@MPB-Tech](http://github.com/MPB-Tech))
|
||||
- Luca Dalli ([@lucadalli](http://github.com/lucadalli))
|
||||
|
||||
## v0.17.7
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.17.6...v0.17.7)
|
||||
|
||||
@@ -35,14 +35,14 @@
|
||||
|
||||
## Demo
|
||||
|
||||
https://github.com/wxt-dev/wxt/assets/10101283/07359e53-f491-43b6-8e8f-fae94aec8063
|
||||
https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94
|
||||
|
||||
## Quick Start
|
||||
|
||||
Bootstrap a new project:
|
||||
|
||||
```sh
|
||||
pnpx wxt@latest init <project-name>
|
||||
pnpm dlx wxt@latest init <project-name>
|
||||
```
|
||||
|
||||
Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
|
||||
|
||||
@@ -18,4 +18,7 @@ export default defineConfig({
|
||||
zip: {
|
||||
downloadPackages: ['sass'],
|
||||
},
|
||||
analysis: {
|
||||
open: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ const chromeExtensionIds = [
|
||||
'agjnjboanicjcpenljmaaigopkgdnihi', // PreMiD
|
||||
'aiakblgmlabokilgljkglggnpflljdgp', // Markdown Sticky Notes
|
||||
'nomnkbngkijpffepcgbbofhcnafpkiep', // DocVersionRedirector
|
||||
'ceicccfeikoipigeghddpocceifjelph', // Plex Skipper
|
||||
];
|
||||
|
||||
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
HTML pages that are bundled and shipped with the extension, but are not included in the manifest.
|
||||
|
||||
If you plan on using the page in an iframe, don't forget to add the page to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
|
||||
|
||||
### Examples
|
||||
|
||||
- Onboarding
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
TypeScript files that are bundled and shipped with the extension, but are not included in the manifest.
|
||||
|
||||
You are responsible for loading/running these scripts where needed.
|
||||
You are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related stylesheets to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources).
|
||||
|
||||
## Filenames
|
||||
|
||||
|
||||
@@ -99,7 +99,17 @@ For a full list of options, see the [API Reference](/api/wxt/interfaces/Extensio
|
||||
|
||||
Normally, to manually reload an extension, you have to visit `chrome://extensions` and click the reload button for your extension.
|
||||
|
||||
When running `wxt` command to start the dev server, WXT adds a keyboard shortcut, `ctrl+E` for Windows/Linux and `cmd+E` for Mac, that reloads the extension when pressed, without visiting `chrome://extensions`.
|
||||
When running `wxt` command to start the dev server, WXT adds a keyboard shortcut `Alt+R`, that reloads the extension when pressed, without visiting `chrome://extensions`. This can also be customized or disabled:
|
||||
|
||||
```ts [wxt.config.ts]
|
||||
import { defineConfig } from 'wxt';
|
||||
|
||||
export default defineConfig({
|
||||
dev: {
|
||||
reloadCommand: 'Alt+T', // false, to disable
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
:::info
|
||||
This shortcut is only available during development, and is not be added to your extension when running `wxt build` or `wxt-zip`.
|
||||
|
||||
@@ -7,13 +7,17 @@ Bootstrap a new project, start from scratch, or [migrate an existing project](/g
|
||||
:::code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpx wxt@latest init <project-name>
|
||||
pnpm dlx wxt@latest init <project-name>
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npx wxt@latest init <project-name>
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bunx wxt@latest init <project-name>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
There are several starting templates available.
|
||||
@@ -55,6 +59,12 @@ cd project-name
|
||||
yarn init
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
mkdir project-name
|
||||
cd project-name
|
||||
bun init
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Then install `wxt`:
|
||||
@@ -73,6 +83,10 @@ npm i --save-dev wxt
|
||||
yarn add --dev wxt
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add --dev wxt
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Add your first entrypoint:
|
||||
@@ -102,7 +116,7 @@ Finally, add scripts to your `package.json`:
|
||||
|
||||
## Migrate an Existing Project
|
||||
|
||||
Before starting the migration, it is recommended to run `pnpx wxt@latest init` to see what a basic project looks like. Once you have an understanding of how WXT projects are structured, you're ready to convert the project, using the initialized project as a reference.
|
||||
Before starting the migration, it is recommended to run `pnpm dlx wxt@latest init` to see what a basic project looks like. Once you have an understanding of how WXT projects are structured, you're ready to convert the project, using the initialized project as a reference.
|
||||
|
||||
Migrating a project to WXT comes down to a few steps:
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Always start by generating a new vanilla project and merging it into your projec
|
||||
|
||||
```sh
|
||||
cd path/to/your/project
|
||||
pnpx wxt@latest init example-wxt --template vanilla
|
||||
pnpm dlx wxt@latest init example-wxt --template vanilla
|
||||
```
|
||||
|
||||
In general, you'll need to:
|
||||
|
||||
@@ -64,12 +64,59 @@ See the [Firefox Addon Store](#firefox-addon-store) section for more details abo
|
||||
|
||||
## GitHub Action
|
||||
|
||||
Here's an example of a GitHub Action to automate submitting new versions of your extension for review. Ensure that you've added all required secrets used in the workflow to the repo's settings.
|
||||
Here's an example of a GitHub Action that submits new versions of an extension for review. Ensure that you've added all required secrets used in the workflow to the repo's settings.
|
||||
|
||||
```yml
|
||||
# TODO
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
submit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 'latest'
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Zip extensions
|
||||
run: |
|
||||
pnpm zip
|
||||
pnpm zip:firefox
|
||||
|
||||
- name: Submit to stores
|
||||
run: |
|
||||
pnpm wxt submit \
|
||||
--chrome-zip .output/*-chrome.zip \
|
||||
--firefox-zip .output/*-firefox.zip --firefox-sources-zip .output/*-sources.zip
|
||||
env:
|
||||
CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
|
||||
CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
|
||||
CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
|
||||
CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
|
||||
FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }}
|
||||
FIREFOX_JWT_ISSUER: ${{ secrets.FIREFOX_JWT_ISSUER }}
|
||||
FIREFOX_JWT_SECRET: ${{ secrets.FIREFOX_JWT_SECRET }}
|
||||
```
|
||||
|
||||
The action above lays the foundation for a basic workflow, including `zip` and `submit` steps. To further enhance your GitHub Action and delve into more complex scenarios, consider exploring the following examples from real projects. They introduce advanced features such as version management, changelog generation, and GitHub releases, tailored for different needs:
|
||||
|
||||
- [`aklinker1/github-better-line-counts`](https://github.com/aklinker1/github-better-line-counts/blob/main/.github/workflows/submit.yml) - Conventional commits, automated version bump and changelog generation, triggered manually, optional dry run for testing
|
||||
- [`GuiEpi/plex-skipper`](https://github.com/GuiEpi/plex-skipper/blob/main/.github/workflows/deploy.yml) - Triggered automatically when `package.json` version is changed, creates and uploads artifacts to GitHub release.
|
||||
|
||||
> These examples are designed to provide clear insights and are a good starting point for customizing your own workflows. Feel free to explore and adapt them to your project needs.
|
||||
|
||||
## Stores
|
||||
|
||||
### Chrome Web Store
|
||||
@@ -124,6 +171,11 @@ yarn
|
||||
yarn zip:firefox
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun i
|
||||
bun zip:firefox
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Make sure the build output is the exact same when running `wxt build -b firefox` in your main project and inside the zipped sources.
|
||||
|
||||
+2
-2
@@ -70,14 +70,14 @@ features:
|
||||
linkText: Read docs
|
||||
---
|
||||
|
||||
<section class="vp-doc">
|
||||
<section class="vp-doc" style="padding: 0 48px">
|
||||
<div class="container">
|
||||
<h2>Put <span style="color: var(--vp-c-brand-1)">Developer Experience</span> First</h2>
|
||||
<p>
|
||||
WXT's simplifies the chrome extension development process by providing tools for zipping and publishing, the best-in-class dev mode, an opinionated project structure, and more. Iterate faster, develop features not build scripts, and use everything the JS ecosystem has to offer.
|
||||
</p>
|
||||
<div style="margin: auto; width: 100%; max-width: 900px; text-align: center">
|
||||
<video src="https://github.com/wxt-dev/wxt/assets/10101283/b32e6766-ec11-45a4-9677-226ee4718e1c" controls></video>
|
||||
<video src="https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94" controls></video>
|
||||
<br />
|
||||
<small>
|
||||
And who doesn't appreciate a beautiful CLI?
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { TestProject } from '../utils';
|
||||
import { resetBundleIncrement } from '~/core/builders/vite/plugins';
|
||||
import open from 'open';
|
||||
|
||||
vi.mock('open');
|
||||
const openMock = vi.mocked(open);
|
||||
|
||||
vi.mock('ci-info', () => ({
|
||||
isCI: false,
|
||||
}));
|
||||
|
||||
describe('Analysis', () => {
|
||||
beforeEach(() => {
|
||||
@@ -89,4 +97,24 @@ describe('Analysis', () => {
|
||||
expect(await project.fileExists('stats/bundle-0.json')).toBe(true);
|
||||
expect(await project.fileExists('stats/bundle-1.json')).toBe(true);
|
||||
});
|
||||
|
||||
it('should open the stats in the browser when requested', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile('entrypoints/popup.html');
|
||||
project.addFile('entrypoints/options.html');
|
||||
project.addFile(
|
||||
'entrypoints/background.ts',
|
||||
'export default defineBackground(() => {});',
|
||||
);
|
||||
|
||||
await project.build({
|
||||
analysis: {
|
||||
enabled: true,
|
||||
open: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(openMock).toBeCalledTimes(1);
|
||||
expect(openMock).toBeCalledWith(project.resolvePath('stats.html'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { TestProject } from '../utils';
|
||||
import { execaCommand } from 'execa';
|
||||
import glob from 'fast-glob';
|
||||
import { mkdir, writeJson } from 'fs-extra';
|
||||
|
||||
describe('Init command', () => {
|
||||
it('should download and create a template', async () => {
|
||||
@@ -42,4 +43,17 @@ describe('Init command', () => {
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should throw an error if the directory is not empty', async () => {
|
||||
const project = new TestProject();
|
||||
await mkdir(project.root);
|
||||
await writeJson(project.resolvePath('package.json'), {});
|
||||
|
||||
await expect(() =>
|
||||
execaCommand(`pnpm -s wxt init ${project.root} -t vue --pm npm`, {
|
||||
env: { ...process.env, CI: 'true' },
|
||||
stdio: 'ignore',
|
||||
}),
|
||||
).rejects.toThrowError('Command failed with exit code 1:');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,4 +71,29 @@ describe('Zipping', () => {
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should correctly apply template variables for zip file names based on provided config', async () => {
|
||||
const project = new TestProject({
|
||||
name: 'test',
|
||||
version: '1.0.0',
|
||||
});
|
||||
project.addFile(
|
||||
'entrypoints/background.ts',
|
||||
'export default defineBackground(() => {});',
|
||||
);
|
||||
const artifactZip = '.output/test-1.0.0-firefox-development.zip';
|
||||
const sourcesZip = '.output/test-1.0.0-development-sources.zip';
|
||||
|
||||
await project.zip({
|
||||
browser: 'firefox',
|
||||
mode: 'development',
|
||||
zip: {
|
||||
artifactTemplate: '{{name}}-{{version}}-{{browser}}-{{mode}}.zip',
|
||||
sourcesTemplate: '{{name}}-{{version}}-{{mode}}-sources.zip',
|
||||
},
|
||||
});
|
||||
|
||||
expect(await project.fileExists(artifactZip)).toBe(true);
|
||||
expect(await project.fileExists(sourcesZip)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
+4
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.17.7",
|
||||
"version": "0.17.9",
|
||||
"description": "Next gen framework for developing web extensions",
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
@@ -115,6 +115,7 @@
|
||||
"c12": "^1.5.1",
|
||||
"cac": "^6.7.14",
|
||||
"chokidar": "^3.5.3",
|
||||
"ci-info": "^4.0.0",
|
||||
"consola": "^3.2.3",
|
||||
"defu": "^6.1.3",
|
||||
"dequal": "^2.0.3",
|
||||
@@ -134,6 +135,7 @@
|
||||
"natural-compare": "^1.4.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
"nypm": "^0.3.6",
|
||||
"open": "^10.1.0",
|
||||
"ora": "^7.0.1",
|
||||
"picocolors": "^1.0.0",
|
||||
"prompts": "^2.4.2",
|
||||
@@ -171,6 +173,7 @@
|
||||
"vitepress": "1.0.0-rc.34",
|
||||
"vitest": "^1.2.2",
|
||||
"vitest-mock-extended": "^1.3.1",
|
||||
"vitest-plugin-random-seed": "^1.0.2",
|
||||
"vue": "^3.3.10"
|
||||
},
|
||||
"packageManager": "pnpm@8.6.3",
|
||||
|
||||
Generated
+57
@@ -35,6 +35,9 @@ importers:
|
||||
chokidar:
|
||||
specifier: ^3.5.3
|
||||
version: 3.6.0
|
||||
ci-info:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
consola:
|
||||
specifier: ^3.2.3
|
||||
version: 3.2.3
|
||||
@@ -92,6 +95,9 @@ importers:
|
||||
nypm:
|
||||
specifier: ^0.3.6
|
||||
version: 0.3.6
|
||||
open:
|
||||
specifier: ^10.1.0
|
||||
version: 10.1.0
|
||||
ora:
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1
|
||||
@@ -198,6 +204,9 @@ importers:
|
||||
vitest-mock-extended:
|
||||
specifier: ^1.3.1
|
||||
version: 1.3.1(typescript@5.3.3)(vitest@1.2.2)
|
||||
vitest-plugin-random-seed:
|
||||
specifier: ^1.0.2
|
||||
version: 1.0.2(vite@5.1.3)
|
||||
vue:
|
||||
specifier: ^3.3.10
|
||||
version: 3.3.10(typescript@5.3.3)
|
||||
@@ -1744,6 +1753,13 @@ packages:
|
||||
run-applescript: 5.0.0
|
||||
dev: false
|
||||
|
||||
/bundle-name@4.1.0:
|
||||
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
run-applescript: 7.0.0
|
||||
dev: false
|
||||
|
||||
/bundle-require@4.0.1(esbuild@0.19.8):
|
||||
resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@@ -1891,6 +1907,11 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/ci-info@4.0.0:
|
||||
resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/citty@0.1.6:
|
||||
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
|
||||
dependencies:
|
||||
@@ -2148,6 +2169,11 @@ packages:
|
||||
untildify: 4.0.0
|
||||
dev: false
|
||||
|
||||
/default-browser-id@5.0.0:
|
||||
resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
|
||||
engines: {node: '>=18'}
|
||||
dev: false
|
||||
|
||||
/default-browser@4.0.0:
|
||||
resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==}
|
||||
engines: {node: '>=14.16'}
|
||||
@@ -2158,6 +2184,14 @@ packages:
|
||||
titleize: 3.0.0
|
||||
dev: false
|
||||
|
||||
/default-browser@5.2.1:
|
||||
resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
bundle-name: 4.1.0
|
||||
default-browser-id: 5.0.0
|
||||
dev: false
|
||||
|
||||
/defaults@1.0.4:
|
||||
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
|
||||
dependencies:
|
||||
@@ -3925,6 +3959,16 @@ packages:
|
||||
dependencies:
|
||||
mimic-fn: 4.0.0
|
||||
|
||||
/open@10.1.0:
|
||||
resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
default-browser: 5.2.1
|
||||
define-lazy-prop: 3.0.0
|
||||
is-inside-container: 1.0.0
|
||||
is-wsl: 3.1.0
|
||||
dev: false
|
||||
|
||||
/open@8.4.2:
|
||||
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -4452,6 +4496,11 @@ packages:
|
||||
execa: 5.1.1
|
||||
dev: false
|
||||
|
||||
/run-applescript@7.0.0:
|
||||
resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
|
||||
engines: {node: '>=18'}
|
||||
dev: false
|
||||
|
||||
/run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
dependencies:
|
||||
@@ -5334,6 +5383,14 @@ packages:
|
||||
vitest: 1.2.2(@types/node@20.10.3)(happy-dom@13.3.8)(sass@1.69.5)
|
||||
dev: true
|
||||
|
||||
/vitest-plugin-random-seed@1.0.2(vite@5.1.3):
|
||||
resolution: {integrity: sha512-Kt5QHmx7I1p2WShsmgJSK/L8hNPZpyNu85zSml5yv/fHDe7gJspcKc7kNYrLVVwZdGcqcZxyHAeH503szzQ95g==}
|
||||
peerDependencies:
|
||||
vite: ^4.0.0 || ^5.0.0
|
||||
dependencies:
|
||||
vite: 5.1.3(@types/node@20.10.3)(sass@1.69.5)
|
||||
dev: true
|
||||
|
||||
/vitest@1.2.2(@types/node@20.10.3)(happy-dom@13.3.8)(sass@1.69.5):
|
||||
resolution: {integrity: sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
||||
+7
-1
@@ -57,6 +57,7 @@ cli
|
||||
.option('--mv3', 'target manifest v3')
|
||||
.option('--mv2', 'target manifest v2')
|
||||
.option('--analyze', 'visualize extension bundle')
|
||||
.option('--analyze-open', 'automatically open stats.html in browser')
|
||||
.action(
|
||||
wrapAction(async (root, flags) => {
|
||||
await build({
|
||||
@@ -66,7 +67,12 @@ cli
|
||||
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
|
||||
configFile: flags.config,
|
||||
debug: flags.debug,
|
||||
analysis: flags.analyze ? { enabled: true } : undefined,
|
||||
analysis: flags.analyze
|
||||
? {
|
||||
enabled: true,
|
||||
open: flags.analyzeOpen,
|
||||
}
|
||||
: undefined,
|
||||
filterEntrypoints: getArrayFromFlags(flags, 'filterEntrypoint'),
|
||||
});
|
||||
}),
|
||||
|
||||
@@ -207,7 +207,7 @@ export interface ContentScriptAnchoredOptions {
|
||||
* In combination with `anchor`, decide how to add the UI to the DOM.
|
||||
*
|
||||
* - `"last"` (default) - Add the UI as the last child of the `anchor` element
|
||||
* - `"first"` - Add the UI as the last child of the `anchor` element
|
||||
* - `"first"` - Add the UI as the first child of the `anchor` element
|
||||
* - `"replace"` - Replace the `anchor` element with the UI.
|
||||
* - `"before"` - Add the UI as the sibling before the `anchor` element
|
||||
* - `"after"` - Add the UI as the sibling after the `anchor` element
|
||||
|
||||
+31
-27
@@ -189,34 +189,38 @@ function createFileReloader(server: WxtDevServer) {
|
||||
|
||||
// Rebuild entrypoints on change
|
||||
const allEntrypoints = await findEntrypoints();
|
||||
const { output: newOutput } = await rebuild(
|
||||
allEntrypoints,
|
||||
// TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
|
||||
changes.rebuildGroups,
|
||||
changes.cachedOutput,
|
||||
);
|
||||
server.currentOutput = newOutput;
|
||||
try {
|
||||
const { output: newOutput } = await rebuild(
|
||||
allEntrypoints,
|
||||
// TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
|
||||
changes.rebuildGroups,
|
||||
changes.cachedOutput,
|
||||
);
|
||||
server.currentOutput = newOutput;
|
||||
|
||||
// Perform reloads
|
||||
switch (changes.type) {
|
||||
case 'extension-reload':
|
||||
server.reloadExtension();
|
||||
consola.success(`Reloaded extension`);
|
||||
break;
|
||||
case 'html-reload':
|
||||
const { reloadedNames } = reloadHtmlPages(
|
||||
changes.rebuildGroups,
|
||||
server,
|
||||
);
|
||||
consola.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
|
||||
break;
|
||||
case 'content-script-reload':
|
||||
reloadContentScripts(changes.changedSteps, server);
|
||||
const rebuiltNames = changes.rebuildGroups
|
||||
.flat()
|
||||
.map((entry) => entry.name);
|
||||
consola.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
|
||||
break;
|
||||
// Perform reloads
|
||||
switch (changes.type) {
|
||||
case 'extension-reload':
|
||||
server.reloadExtension();
|
||||
consola.success(`Reloaded extension`);
|
||||
break;
|
||||
case 'html-reload':
|
||||
const { reloadedNames } = reloadHtmlPages(
|
||||
changes.rebuildGroups,
|
||||
server,
|
||||
);
|
||||
consola.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
|
||||
break;
|
||||
case 'content-script-reload':
|
||||
reloadContentScripts(changes.changedSteps, server);
|
||||
const rebuiltNames = changes.rebuildGroups
|
||||
.flat()
|
||||
.map((entry) => entry.name);
|
||||
consola.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
// Catch build errors instead of crashing. Don't log error either, builder should have already logged it
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
+12
-1
@@ -59,6 +59,16 @@ export async function initialize(options: {
|
||||
input.template ??= defaultTemplate;
|
||||
input.packageManager ??= options.packageManager;
|
||||
|
||||
const isExists = await fs.pathExists(input.directory);
|
||||
if (isExists) {
|
||||
const isEmpty = (await fs.readdir(input.directory)).length === 0;
|
||||
if (!isEmpty) {
|
||||
consola.error(
|
||||
`The directory ${path.resolve(input.directory)} is not empty. Aborted.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
await cloneProject(input);
|
||||
|
||||
const cdPath = path.relative(process.cwd(), path.resolve(input.directory));
|
||||
@@ -117,7 +127,8 @@ async function listTemplates(): Promise<Template[]> {
|
||||
return l.name.localeCompare(r.name);
|
||||
});
|
||||
} catch (err) {
|
||||
throw Error(`Cannot load templates: ${JSON.stringify(err, null, 2)}`);
|
||||
consola.error(err);
|
||||
throw Error(`Failed to load templates`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,45 +6,41 @@ import { exists } from 'fs-extra';
|
||||
|
||||
const cwd = path.resolve(__dirname, 'fixtures/npm-project');
|
||||
|
||||
describe(
|
||||
'NPM Package Management Utils',
|
||||
() => {
|
||||
beforeAll(async () => {
|
||||
// NPM needs the modules installed for 'npm ls' to work
|
||||
await execaCommand('npm i', { cwd });
|
||||
describe('NPM Package Management Utils', () => {
|
||||
beforeAll(async () => {
|
||||
// NPM needs the modules installed for 'npm ls' to work
|
||||
await execaCommand('npm i', { cwd });
|
||||
}, 60e3);
|
||||
|
||||
describe('listDependencies', () => {
|
||||
it('should list direct dependencies', async () => {
|
||||
const actual = await npm.listDependencies({ cwd });
|
||||
expect(actual).toEqual([
|
||||
{ name: 'flatten', version: '1.0.3' },
|
||||
{ name: 'mime-types', version: '2.1.35' },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('listDependencies', () => {
|
||||
it('should list direct dependencies', async () => {
|
||||
const actual = await npm.listDependencies({ cwd });
|
||||
expect(actual).toEqual([
|
||||
{ name: 'flatten', version: '1.0.3' },
|
||||
{ name: 'mime-types', version: '2.1.35' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should list all dependencies', async () => {
|
||||
const actual = await npm.listDependencies({ cwd, all: true });
|
||||
expect(actual).toEqual([
|
||||
{ name: 'flatten', version: '1.0.3' },
|
||||
{ name: 'mime-types', version: '2.1.35' },
|
||||
{ name: 'mime-db', version: '1.52.0' },
|
||||
]);
|
||||
});
|
||||
it('should list all dependencies', async () => {
|
||||
const actual = await npm.listDependencies({ cwd, all: true });
|
||||
expect(actual).toEqual([
|
||||
{ name: 'flatten', version: '1.0.3' },
|
||||
{ name: 'mime-types', version: '2.1.35' },
|
||||
{ name: 'mime-db', version: '1.52.0' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadDependency', () => {
|
||||
it('should download the dependency as a tarball', async () => {
|
||||
const downloadDir = path.resolve(cwd, 'dist');
|
||||
const id = 'mime-db@1.52.0';
|
||||
const expected = path.resolve(downloadDir, 'mime-db-1.52.0.tgz');
|
||||
describe('downloadDependency', () => {
|
||||
it('should download the dependency as a tarball', async () => {
|
||||
const downloadDir = path.resolve(cwd, 'dist');
|
||||
const id = 'mime-db@1.52.0';
|
||||
const expected = path.resolve(downloadDir, 'mime-db-1.52.0.tgz');
|
||||
|
||||
const actual = await npm.downloadDependency(id, downloadDir);
|
||||
const actual = await npm.downloadDependency(id, downloadDir);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
expect(await exists(actual)).toBe(true);
|
||||
});
|
||||
expect(actual).toEqual(expected);
|
||||
expect(await exists(actual)).toBe(true);
|
||||
});
|
||||
},
|
||||
{ timeout: 20e3 },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ export async function buildEntrypoints(
|
||||
try {
|
||||
steps.push(await wxt.config.builder.build(group));
|
||||
} catch (err) {
|
||||
spinner.stop().clear();
|
||||
wxt.logger.error(err);
|
||||
throw Error(`Failed to build ${groupNames.join(', ')}`, { cause: err });
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import consola from 'consola';
|
||||
import { wxt } from '../../wxt';
|
||||
import { mergeJsonOutputs } from '@aklinker1/rollup-plugin-visualizer';
|
||||
import { isCI } from 'ci-info';
|
||||
|
||||
/**
|
||||
* Builds the extension based on an internal config. No more config discovery is performed, the
|
||||
@@ -81,6 +82,15 @@ export async function internalBuild(): Promise<BuildOutput> {
|
||||
wxt.logger.info(
|
||||
`Analysis complete:\n ${pc.gray('└─')} ${pc.yellow(statsPath)}`,
|
||||
);
|
||||
if (wxt.config.analysis.open) {
|
||||
if (isCI) {
|
||||
wxt.logger.debug(`Skipped opening ${pc.yellow(statsPath)} in CI`);
|
||||
} else {
|
||||
wxt.logger.info(`Opening ${pc.yellow(statsPath)} in browser...`);
|
||||
const { default: open } = await import('open');
|
||||
open(wxt.config.analysis.outputFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
WxtDevServer,
|
||||
WxtResolvedUnimportOptions,
|
||||
Logger,
|
||||
WxtCommand,
|
||||
} from '~/types';
|
||||
import path from 'node:path';
|
||||
import { createFsCache } from '~/core/utils/cache';
|
||||
@@ -30,7 +31,7 @@ import { normalizePath } from '../paths';
|
||||
*/
|
||||
export async function resolveConfig(
|
||||
inlineConfig: InlineConfig,
|
||||
command: 'build' | 'serve',
|
||||
command: WxtCommand,
|
||||
server?: WxtDevServer,
|
||||
): Promise<ResolvedConfig> {
|
||||
// Load user config
|
||||
@@ -65,8 +66,7 @@ export async function resolveConfig(
|
||||
const manifestVersion =
|
||||
mergedConfig.manifestVersion ??
|
||||
(browser === 'firefox' || browser === 'safari' ? 2 : 3);
|
||||
const mode =
|
||||
mergedConfig.mode ?? (command === 'build' ? 'production' : 'development');
|
||||
const mode = mergedConfig.mode ?? COMMAND_MODES[command];
|
||||
const env: ConfigEnv = { browser, command, manifestVersion, mode };
|
||||
|
||||
const root = path.resolve(
|
||||
@@ -113,13 +113,6 @@ export async function resolveConfig(
|
||||
}).map(([key, value]) => [key, path.resolve(root, value)]),
|
||||
);
|
||||
|
||||
const analysisOutputFile = path.resolve(
|
||||
root,
|
||||
mergedConfig.analysis?.outputFile ?? 'stats.html',
|
||||
);
|
||||
const analysisOutputDir = path.dirname(analysisOutputFile);
|
||||
const analysisOutputName = path.parse(analysisOutputFile).name;
|
||||
|
||||
const finalConfig: Omit<ResolvedConfig, 'builder'> = {
|
||||
browser,
|
||||
command,
|
||||
@@ -142,25 +135,14 @@ export async function resolveConfig(
|
||||
srcDir,
|
||||
typesDir,
|
||||
wxtDir,
|
||||
zip: resolveInternalZipConfig(root, mergedConfig),
|
||||
transformManifest(manifest) {
|
||||
userConfig.transformManifest?.(manifest);
|
||||
inlineConfig.transformManifest?.(manifest);
|
||||
},
|
||||
analysis: {
|
||||
enabled: mergedConfig.analysis?.enabled ?? false,
|
||||
template: mergedConfig.analysis?.template ?? 'treemap',
|
||||
outputFile: analysisOutputFile,
|
||||
outputDir: analysisOutputDir,
|
||||
outputName: analysisOutputName,
|
||||
keepArtifacts: mergedConfig.analysis?.keepArtifacts ?? false,
|
||||
},
|
||||
zip: resolveZipConfig(root, mergedConfig),
|
||||
transformManifest: mergedConfig.transformManifest,
|
||||
analysis: resolveAnalysisConfig(root, mergedConfig),
|
||||
userConfigMetadata: userConfigMetadata ?? {},
|
||||
alias,
|
||||
experimental: {
|
||||
includeBrowserPolyfill:
|
||||
mergedConfig.experimental?.includeBrowserPolyfill ?? true,
|
||||
},
|
||||
experimental: defu(mergedConfig.experimental, {
|
||||
includeBrowserPolyfill: true,
|
||||
}),
|
||||
server,
|
||||
dev: {
|
||||
reloadCommand,
|
||||
@@ -195,74 +177,40 @@ async function resolveManifestConfig(
|
||||
function mergeInlineConfig(
|
||||
inlineConfig: InlineConfig,
|
||||
userConfig: UserConfig,
|
||||
): NullablyRequired<InlineConfig> {
|
||||
let imports: InlineConfig['imports'];
|
||||
if (inlineConfig.imports === false || userConfig.imports === false) {
|
||||
imports = false;
|
||||
} else if (userConfig.imports == null && inlineConfig.imports == null) {
|
||||
imports = undefined;
|
||||
} else {
|
||||
imports = defu(inlineConfig.imports ?? {}, userConfig.imports ?? {});
|
||||
}
|
||||
): InlineConfig {
|
||||
// Merge imports option
|
||||
const imports: InlineConfig['imports'] =
|
||||
inlineConfig.imports === false || userConfig.imports === false
|
||||
? false
|
||||
: userConfig.imports == null && inlineConfig.imports == null
|
||||
? undefined
|
||||
: defu(inlineConfig.imports ?? {}, userConfig.imports ?? {});
|
||||
|
||||
// Merge manifest option
|
||||
const manifest: UserManifestFn = async (env) => {
|
||||
const user = await resolveManifestConfig(env, userConfig.manifest);
|
||||
const inline = await resolveManifestConfig(env, inlineConfig.manifest);
|
||||
return defu(inline, user);
|
||||
};
|
||||
const runner: InlineConfig['runner'] = defu(
|
||||
inlineConfig.runner ?? {},
|
||||
userConfig.runner ?? {},
|
||||
);
|
||||
const zip: InlineConfig['zip'] = defu(
|
||||
inlineConfig.zip ?? {},
|
||||
userConfig.zip ?? {},
|
||||
);
|
||||
const hooks: InlineConfig['hooks'] = defu(
|
||||
inlineConfig.hooks ?? {},
|
||||
userConfig.hooks ?? {},
|
||||
);
|
||||
|
||||
// Merge transformManifest option
|
||||
const transformManifest: InlineConfig['transformManifest'] = (manifest) => {
|
||||
userConfig.transformManifest?.(manifest);
|
||||
inlineConfig.transformManifest?.(manifest);
|
||||
};
|
||||
|
||||
return {
|
||||
root: inlineConfig.root ?? userConfig.root,
|
||||
browser: inlineConfig.browser ?? userConfig.browser,
|
||||
manifestVersion: inlineConfig.manifestVersion ?? userConfig.manifestVersion,
|
||||
configFile: inlineConfig.configFile,
|
||||
debug: inlineConfig.debug ?? userConfig.debug,
|
||||
entrypointsDir: inlineConfig.entrypointsDir ?? userConfig.entrypointsDir,
|
||||
filterEntrypoints:
|
||||
inlineConfig.filterEntrypoints ?? userConfig.filterEntrypoints,
|
||||
...defu(inlineConfig, userConfig),
|
||||
// Custom merge values
|
||||
transformManifest,
|
||||
imports,
|
||||
logger: inlineConfig.logger ?? userConfig.logger,
|
||||
manifest,
|
||||
mode: inlineConfig.mode ?? userConfig.mode,
|
||||
publicDir: inlineConfig.publicDir ?? userConfig.publicDir,
|
||||
runner,
|
||||
srcDir: inlineConfig.srcDir ?? userConfig.srcDir,
|
||||
outDir: inlineConfig.outDir ?? userConfig.outDir,
|
||||
zip,
|
||||
analysis: {
|
||||
...userConfig.analysis,
|
||||
...inlineConfig.analysis,
|
||||
},
|
||||
alias: {
|
||||
...userConfig.alias,
|
||||
...inlineConfig.alias,
|
||||
},
|
||||
experimental: {
|
||||
...userConfig.experimental,
|
||||
...inlineConfig.experimental,
|
||||
},
|
||||
// Vite builder handles merging vite config internally
|
||||
vite: undefined,
|
||||
transformManifest: undefined,
|
||||
dev: {
|
||||
...userConfig.dev,
|
||||
...inlineConfig.dev,
|
||||
},
|
||||
hooks,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveInternalZipConfig(
|
||||
function resolveZipConfig(
|
||||
root: string,
|
||||
mergedConfig: InlineConfig,
|
||||
): NullablyRequired<ResolvedConfig['zip']> {
|
||||
@@ -291,6 +239,28 @@ function resolveInternalZipConfig(
|
||||
};
|
||||
}
|
||||
|
||||
function resolveAnalysisConfig(
|
||||
root: string,
|
||||
mergedConfig: InlineConfig,
|
||||
): NullablyRequired<ResolvedConfig['analysis']> {
|
||||
const analysisOutputFile = path.resolve(
|
||||
root,
|
||||
mergedConfig.analysis?.outputFile ?? 'stats.html',
|
||||
);
|
||||
const analysisOutputDir = path.dirname(analysisOutputFile);
|
||||
const analysisOutputName = path.parse(analysisOutputFile).name;
|
||||
|
||||
return {
|
||||
enabled: mergedConfig.analysis?.enabled ?? false,
|
||||
open: mergedConfig.analysis?.open ?? false,
|
||||
template: mergedConfig.analysis?.template ?? 'treemap',
|
||||
outputFile: analysisOutputFile,
|
||||
outputDir: analysisOutputDir,
|
||||
outputName: analysisOutputName,
|
||||
keepArtifacts: mergedConfig.analysis?.keepArtifacts ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
async function getUnimportOptions(
|
||||
wxtDir: string,
|
||||
logger: Logger,
|
||||
@@ -359,3 +329,11 @@ function logMissingDir(logger: Logger, name: string, expected: string) {
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of `ConfigEnv` commands to their default modes.
|
||||
*/
|
||||
const COMMAND_MODES: Record<WxtCommand, string> = {
|
||||
build: 'production',
|
||||
serve: 'development',
|
||||
};
|
||||
|
||||
@@ -111,7 +111,7 @@ export async function generateManifest(
|
||||
if (wxt.config.command === 'serve') addDevModePermissions(manifest);
|
||||
|
||||
// TODO: Remove in v1
|
||||
wxt.config.transformManifest(manifest);
|
||||
wxt.config.transformManifest?.(manifest);
|
||||
await wxt.hooks.callHook('build:manifestGenerated', wxt, manifest);
|
||||
|
||||
if (wxt.config.manifestVersion === 2) {
|
||||
|
||||
@@ -27,7 +27,7 @@ import { mock } from 'vitest-mock-extended';
|
||||
import { vi } from 'vitest';
|
||||
import { setWxtForTesting } from '~/core/wxt';
|
||||
|
||||
faker.seed(__TEST_SEED__);
|
||||
faker.seed(import.meta.test.SEED);
|
||||
|
||||
type DeepPartial<T> = T extends object
|
||||
? {
|
||||
@@ -273,6 +273,7 @@ export const fakeResolvedConfig = fakeObjectCreator<ResolvedConfig>(() => {
|
||||
server: mock<WxtDevServer>(),
|
||||
analysis: {
|
||||
enabled: false,
|
||||
open: false,
|
||||
template: 'treemap',
|
||||
outputFile: fakeFile(),
|
||||
outputDir: fakeDir(),
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { InlineConfig, Wxt, WxtDevServer, WxtHooks } from '~/types';
|
||||
import { InlineConfig, Wxt, WxtCommand, WxtDevServer, WxtHooks } from '~/types';
|
||||
import { resolveConfig } from './utils/building';
|
||||
import { createHooks } from 'hookable';
|
||||
import { createWxtPackageManager } from './package-managers';
|
||||
@@ -13,7 +13,7 @@ export let wxt: Wxt;
|
||||
* Create and register a global instance of the Wxt interface for use throughout the project.
|
||||
*/
|
||||
export async function registerWxt(
|
||||
command: 'build' | 'serve',
|
||||
command: WxtCommand,
|
||||
inlineConfig: InlineConfig = {},
|
||||
server?: WxtDevServer,
|
||||
): Promise<void> {
|
||||
|
||||
@@ -38,6 +38,7 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
|
||||
'{{version}}',
|
||||
output.manifest.version_name ?? output.manifest.version,
|
||||
)
|
||||
.replaceAll('{{mode}}', wxt.config.mode)
|
||||
.replaceAll('{{manifestVersion}}', `mv${wxt.config.manifestVersion}`);
|
||||
|
||||
await fs.ensureDir(wxt.config.outBaseDir);
|
||||
|
||||
Vendored
+1
-1
@@ -4,7 +4,7 @@ declare const __DEV_SERVER_PORT__: string;
|
||||
|
||||
// Globals defined by the vite-plugins/devServerGlobals.ts and utils/globals.ts
|
||||
interface ImportMetaEnv {
|
||||
readonly COMMAND: 'build' | 'serve';
|
||||
readonly COMMAND: WxtCommand;
|
||||
readonly MANIFEST_VERSION: 2 | 3;
|
||||
readonly ENTRYPOINT: string;
|
||||
}
|
||||
|
||||
+25
-11
@@ -117,10 +117,11 @@ export interface InlineConfig {
|
||||
*
|
||||
* Available template variables:
|
||||
*
|
||||
* - `{{name}}` - The project's name converted to kebab-case
|
||||
* - `{{version}}` - The version_name or version from the manifest
|
||||
* - `{{browser}}` - The target browser from the `--browser` CLI flag
|
||||
* - `{{manifestVersion}}` - Either "2" or "3"
|
||||
* - <span v-pre>`{{name}}`</span> - The project's name converted to kebab-case
|
||||
* - <span v-pre>`{{version}}`</span> - The version_name or version from the manifest
|
||||
* - <span v-pre>`{{browser}}`</span> - The target browser from the `--browser` CLI flag
|
||||
* - <span v-pre>`{{mode}}`</span> - The current mode
|
||||
* - <span v-pre>`{{manifestVersion}}`</span> - Either "2" or "3"
|
||||
*
|
||||
* @default "{{name}}-{{version}}-{{browser}}.zip"
|
||||
*/
|
||||
@@ -130,10 +131,11 @@ export interface InlineConfig {
|
||||
*
|
||||
* Available template variables:
|
||||
*
|
||||
* - `{{name}}` - The project's name converted to kebab-case
|
||||
* - `{{version}}` - The version_name or version from the manifest
|
||||
* - `{{browser}}` - The target browser from the `--browser` CLI flag
|
||||
* - `{{manifestVersion}}` - Either "2" or "3"
|
||||
* - <span v-pre>`{{name}}`</span> - The project's name converted to kebab-case
|
||||
* - <span v-pre>`{{version}}`</span> - The version_name or version from the manifest
|
||||
* - <span v-pre>`{{browser}}`</span> - The target browser from the `--browser` CLI flag
|
||||
* - <span v-pre>`{{mode}}`</span> - The current mode
|
||||
* - <span v-pre>`{{manifestVersion}}`</span> - Either "2" or "3"
|
||||
*
|
||||
* @default "{{name}}-{{version}}-sources.zip"
|
||||
*/
|
||||
@@ -229,6 +231,12 @@ export interface InlineConfig {
|
||||
* @default false
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
* Set to true to automatically open the `stats.html` file when the build is finished. When building in CI, the browser will never open.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
open?: boolean;
|
||||
/**
|
||||
* When running `wxt build --analyze` or setting `analysis.enabled` to true, customize how the
|
||||
* bundle will be visualized. See
|
||||
@@ -766,7 +774,7 @@ export interface ConfigEnv {
|
||||
/**
|
||||
* The command used to run WXT. `"serve"` during development and `"build"` for any other command.
|
||||
*/
|
||||
command: 'build' | 'serve';
|
||||
command: WxtCommand;
|
||||
/**
|
||||
* Browser passed in from the CLI via the `-b` or `--browser` flag. Defaults to `"chrome"` when not passed.
|
||||
*/
|
||||
@@ -779,6 +787,8 @@ export interface ConfigEnv {
|
||||
manifestVersion: 2 | 3;
|
||||
}
|
||||
|
||||
export type WxtCommand = 'build' | 'serve';
|
||||
|
||||
/**
|
||||
* Configure how the browser starts up.
|
||||
*/
|
||||
@@ -1010,7 +1020,7 @@ export interface ResolvedConfig {
|
||||
*/
|
||||
wxtModuleDir: string;
|
||||
mode: string;
|
||||
command: 'build' | 'serve';
|
||||
command: WxtCommand;
|
||||
browser: TargetBrowser;
|
||||
manifestVersion: TargetManifestVersion;
|
||||
env: ConfigEnv;
|
||||
@@ -1030,9 +1040,13 @@ export interface ResolvedConfig {
|
||||
downloadedPackagesDir: string;
|
||||
downloadPackages: string[];
|
||||
};
|
||||
transformManifest: (manifest: Manifest.WebExtensionManifest) => void;
|
||||
/**
|
||||
* @deprecated Use `build:manifestGenerated` hook instead.
|
||||
*/
|
||||
transformManifest?: (manifest: Manifest.WebExtensionManifest) => void;
|
||||
analysis: {
|
||||
enabled: boolean;
|
||||
open: boolean;
|
||||
template: NonNullable<PluginVisualizerOptions['template']>;
|
||||
/** Absolute file path to the `stats.html` file */
|
||||
outputFile: string;
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
declare const __TEST_SEED__: number;
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vue-tsc": "^1.8.27",
|
||||
"vue-tsc": "^2.0.6",
|
||||
"wxt": "^0.17.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["vitest-plugin-random-seed/types"],
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
}
|
||||
|
||||
+3
-16
@@ -1,20 +1,7 @@
|
||||
import { defineWorkspace } from 'vitest/config';
|
||||
import fs from 'fs-extra';
|
||||
import pc from 'picocolors';
|
||||
import type { Plugin } from 'vite';
|
||||
import path from 'node:path';
|
||||
|
||||
const seed = Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
|
||||
console.info('Test seed: ' + pc.cyan(seed));
|
||||
|
||||
// config.define doesn't work with workspaces, so we have to set it inside a plugin
|
||||
const testSeed = (): Plugin => ({
|
||||
name: 'test-seed',
|
||||
config(config) {
|
||||
config.define ??= {};
|
||||
config.define.__TEST_SEED__ = JSON.stringify(seed);
|
||||
},
|
||||
});
|
||||
import RandomSeed from 'vitest-plugin-random-seed';
|
||||
|
||||
const resolve = {
|
||||
alias: {
|
||||
@@ -36,7 +23,7 @@ export default defineWorkspace([
|
||||
restoreMocks: true,
|
||||
setupFiles: 'vitest.setup.ts',
|
||||
},
|
||||
plugins: [testSeed()],
|
||||
plugins: [RandomSeed()],
|
||||
resolve,
|
||||
},
|
||||
{
|
||||
@@ -45,7 +32,7 @@ export default defineWorkspace([
|
||||
dir: 'e2e',
|
||||
testTimeout: 120e3,
|
||||
},
|
||||
plugins: [testSeed()],
|
||||
plugins: [RandomSeed()],
|
||||
resolve,
|
||||
},
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user