Compare commits

...

56 Commits

Author SHA1 Message Date
GitHub Actions 4dd6320eae chore(release): v0.4.0 2023-09-05 15:07:59 +00:00
Aaron a8285fff78 feat: Generate types for browser.i18n.getMessage (#103) 2023-09-05 09:55:55 -05:00
Aaron d89be2544d chore: Use import aliases in Vue template (#104) 2023-09-05 09:31:47 -05:00
Aaron f234c85357 fix!: Use relative path aliases inside .wxt/tsconfig.json (#102)
BREAKING CHANGE: Moved .wxt directory from inside srcDir to root to match Nuxt.
2023-09-04 21:48:07 -05:00
Aaron Klinker 93bfee0578 chore: Remove explict icon config from templates
Icons are now automatically discovered thanks to aefc8d3
2023-09-04 15:55:29 -05:00
Aaron Klinker 2c7922ca4c chore: Fix test watcher restarting indefinetly 2023-09-04 15:41:06 -05:00
dependabot[bot] 72ae812c42 chore(deps-dev): bump vitepress from 1.0.0-rc.4 to 1.0.0-rc.10 (#96)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2023-09-03 16:59:04 -05:00
Aaron 2a0842bb62 chore: Increase E2E test timeout because GitHub Actions Window runner is slow 2023-09-02 11:22:34 -05:00
Aaron b301ee324e feat: Replace web-ext with web-ext-run (#101) 2023-09-02 09:47:05 -05:00
dependabot[bot] 4ef3eeaca1 chore(deps-dev): bump vitest from 0.34.1 to 0.34.3 (#99)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-01 17:29:21 -05:00
dependabot[bot] e803bfecb5 chore(deps-dev): bump lint-staged from 14.0.0 to 14.0.1 (#100)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-01 17:06:35 -05:00
dependabot[bot] 0dcf540393 chore(deps): bump linkedom from 0.15.1 to 0.15.2 (#98)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-01 17:05:51 -05:00
dependabot[bot] 062e25d834 chore(deps-dev): bump vitest-mock-extended from 1.2.0 to 1.2.1 (#97)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-01 17:03:19 -05:00
Aaron Klinker b58a15e783 chore: Fix failing demo build 2023-08-29 16:06:43 -05:00
Aaron Klinker 5f5f1d90f8 fix: Don't overwrite wxt.config.ts content scripts, append entrypoints to it 2023-08-29 16:02:47 -05:00
Aaron Klinker 83e62a1d63 chore: Fix remote code E2E test 2023-08-29 16:02:47 -05:00
Aaron Klinker b428a62a90 fix: Allow adding custom content scripts 2023-08-29 15:48:25 -05:00
dependabot[bot] 1fe4eedc9a chore(deps): bump filesize from 10.0.8 to 10.0.12 (#90)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 21:07:26 -05:00
dependabot[bot] 39d3192904 chore(deps-dev): bump lint-staged from 13.3.0 to 14.0.0 (#89)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 21:07:18 -05:00
dependabot[bot] de30509d94 chore(deps-dev): bump vitest-mock-extended from 1.1.4 to 1.2.0 (#87)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 21:07:11 -05:00
dependabot[bot] d5ae2332ca chore(deps): bump linkedom from 0.14.26 to 0.15.1 (#86)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 20:59:18 -05:00
dependabot[bot] f8b367dd7b chore(deps): bump unimport from 3.1.0 to 3.1.3 (#88)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 20:58:07 -05:00
dependabot[bot] 6bcb71baa0 chore(deps-dev): bump vitepress from 1.0.0-beta.5 to 1.0.0-rc.4 (#85)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2023-08-14 20:56:33 -05:00
dependabot[bot] 54bdf54839 chore(deps-dev): bump @vitest/coverage-v8 from 0.32.4 to 0.34.1 (#84)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 20:47:43 -05:00
dependabot[bot] 83f457f40f chore(deps): bump vite from 4.4.7 to 4.4.9 (#82)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron Klinker <aaronklinker1@gmail.com>
2023-08-13 19:30:02 -05:00
dependabot[bot] f9df2d0809 chore(deps-dev): bump tsup from 7.1.0 to 7.2.0 (#80)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-13 15:07:49 -05:00
dependabot[bot] 2b079bb7f6 chore(deps-dev): bump @types/node from 20.4.5 to 20.5.0 (#78)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-13 15:06:03 -05:00
Aaron 0194448af4 Remove generated docs/config.md file (#83) 2023-08-13 14:59:34 -05:00
dependabot[bot] b409def211 chore(deps-dev): bump ora from 6.3.1 to 7.0.1 (#79) 2023-08-13 14:58:02 -05:00
dependabot[bot] 62bbef3487 chore(deps-dev): bump vitest from 0.32.4 to 0.34.1 (#81) 2023-08-13 14:56:47 -05:00
Aaron 87013bc616 Check for updates weekly 2023-08-13 14:52:45 -05:00
Aaron Klinker 4599bf2aa0 Fix dependabot config 2023-08-13 14:41:40 -05:00
Aaron d629acf518 chore: Upgrade to prettier 3 (#77) 2023-08-13 14:39:53 -05:00
Aaron 2bde9175b7 chore: Setup bug and feature issue templates 2023-08-13 14:39:33 -05:00
Aaron 8d5f4f5201 docs: Add contribution guide (#76) 2023-08-13 14:22:17 -05:00
Aaron d05f126eb3 feat: Add --debug flag for printing debug logs for all CLI commands (#75) 2023-08-13 14:04:43 -05:00
Aaron Klinker e164bd51fe chore: Update social preview 2023-08-13 13:18:01 -05:00
Aaron d66293c56e chore: Setup dependabot for upgrading dependencies 2023-08-13 13:15:46 -05:00
GitHub Actions 6aa827438c chore(release): v0.3.2 2023-08-13 17:58:07 +00:00
Aaron 6ee8a43677 docs: Generate markdown for config reference (#74) 2023-08-13 12:51:19 -05:00
Aaron Klinker d54d6111e6 feat: Don't allow auto-importing from subdirectories
This reverts commit 547fee0e0e.

BREAKING CHANGE: 547fee previously added auto imports from subdirectories, but Nuxt does not do this, so to be consistent with Nuxt's DX, I'm also removing these auto-imports.
2023-08-13 10:13:42 -05:00
Aaron aefc8d3167 feat: Discover icons from the public directory (#72) 2023-08-13 10:05:27 -05:00
Aaron 7ccff533ff chore: Improve file list output in CI (#73) 2023-08-13 09:51:22 -05:00
Aaron Klinker edfa075030 ci: Validate templates using tarball to avoid version conflicts within the wxt/node_modules directory 2023-08-13 08:33:26 -05:00
Aaron Klinker ef140dc7f8 ci: List vite version when validating project templates 2023-08-13 08:33:26 -05:00
Aaron Klinker 39467d10f3 docs: Fix capitalization 2023-08-13 08:33:26 -05:00
Aaron Klinker 97f0938c99 docs: Fix typos 2023-08-12 09:21:40 -05:00
Aaron Klinker 18741eae8e Add remote code page to sidebar 2023-07-30 00:06:09 -05:00
Aaron Klinker 3f3ef37c96 Cleanup nuxt similarities 2023-07-30 00:05:14 -05:00
Aaron Klinker 323045a7c3 docs: Document the url: import prefix for remote code 2023-07-29 23:33:28 -05:00
Aaron 9b34180467 ci: Validate templates against main (#66) 2023-07-29 23:21:13 -05:00
Aaron Klinker 7f15305037 chore: Update templates to work with CSS entrypoints 2023-07-29 23:08:42 -05:00
Aaron Klinker 35cc1c94bf Update contributor links in changelog 2023-07-29 22:57:45 -05:00
Aaron Klinker 547c1850ac chore: Upgrade vite (v4.3 &rarr; v4.4) 2023-07-29 22:50:08 -05:00
Aaron Klinker 798f02f626 chore: Upgrade dependencies 2023-07-29 22:49:00 -05:00
Aaron Klinker d5948abfe5 Update changelog 2023-07-29 22:39:42 -05:00
96 changed files with 1894 additions and 2189 deletions
+42
View File
@@ -0,0 +1,42 @@
---
name: Bug report
about: Report an issue with WXT
title: ''
labels: triage
assignees: ''
---
### Describe the bug
A clear and concise description of what the bug is.
### To Reproduce
Upload a ZIP or share a link to a repo representing the minimal reproduction. **_If you don't upload a minimal reproduction, you bug report will be closed._**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
### Expected behavior
A clear and concise description of what you expected to happen.
### Screenshots
If applicable, add screenshots to help explain your problem.
### Environment
<!--- Run `npx envinfo --system --browsers --binaries --npmPackages wxt,vite` and paste the output below -->
```
Paste output here
```
### Additional context
Add any other context about the problem here, otherwise you can delete this section.
+23
View File
@@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea for WXT
title: ''
labels: feature
assignees: ''
---
### Feature Request
Please describe your feature, be clear and concise. If you have a proposal for required type or API changes, list them here.
#### Is your feature request related to a bug?
If so, add a link here. If not, write "N/A"
### What are the alternatives?
A clear and concise description of any alternative solutions or features you've considered.
### Additional context
Add any other context or screenshots about the feature request here.
+11
View File
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: npm
directory: '/' # Location of package manifests
schedule:
interval: 'monthly'
+13
View File
@@ -72,10 +72,15 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Build Local Tarball
run: pnpm pack
- name: Validate Vanilla
working-directory: templates/vanilla
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
@@ -83,6 +88,8 @@ jobs:
working-directory: templates/vue
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
@@ -90,6 +97,8 @@ jobs:
working-directory: templates/react
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
@@ -97,6 +106,8 @@ jobs:
working-directory: templates/svelte
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run check
@@ -104,5 +115,7 @@ jobs:
working-directory: templates/solid
run: |
npm i
npm i -D ../../wxt-*.tgz
npm ls vite
npm run build
npm run compile
+1 -1
View File
@@ -10,7 +10,6 @@
/docs/.vitepress/cache
coverage
dist
e2e/project
node_modules
TODOs.md
web-ext.config.js
@@ -18,3 +17,4 @@ web-ext.config.ts
templates/*/pnpm-lock.yaml
templates/*/yarn.lock
templates/*/package-lock.json
docs/config.md
+3 -1
View File
@@ -1,8 +1,10 @@
.output
coverage
dist
e2e/project
.wxt
docs/.vitepress/cache
pnpm-lock.yaml
CHANGELOG.md
.gitattributes
.gitignore
.prettierignore
-1
View File
@@ -1,3 +1,2 @@
singleQuote: true
trailingComma: all
endOfLine: lf
+106 -20
View File
@@ -1,5 +1,93 @@
# Changelog
## v0.4.0
[compare changes](https://github.com/aklinker1/wxt/compare/v0.3.2...v0.4.0)
### 🚀 Enhancements
- Add `--debug` flag for printing debug logs for all CLI commands ([#75](https://github.com/aklinker1/wxt/pull/75))
- Replace `web-ext` with `web-ext-run` ([#101](https://github.com/aklinker1/wxt/pull/101))
- Generate types for `browser.i18n.getMessage` ([#103](https://github.com/aklinker1/wxt/pull/103))
### 🩹 Fixes
- Allow adding custom content scripts ([b428a62](https://github.com/aklinker1/wxt/commit/b428a62))
- Don't overwrite `wxt.config.ts` content scripts, append entrypoints to it ([5f5f1d9](https://github.com/aklinker1/wxt/commit/5f5f1d9))
- ⚠️ Use relative path aliases inside `.wxt/tsconfig.json` ([#102](https://github.com/aklinker1/wxt/pull/102))
### 📖 Documentation
- Add contribution guide ([#76](https://github.com/aklinker1/wxt/pull/76))
### 🏡 Chore
- Setup dependabot for upgrading dependencies ([d66293c](https://github.com/aklinker1/wxt/commit/d66293c))
- Update social preview ([e164bd5](https://github.com/aklinker1/wxt/commit/e164bd5))
- Setup bug and feature issue templates ([2bde917](https://github.com/aklinker1/wxt/commit/2bde917))
- Upgrade to prettier 3 ([#77](https://github.com/aklinker1/wxt/pull/77))
- **deps-dev:** Bump vitest from 0.32.4 to 0.34.1 ([#81](https://github.com/aklinker1/wxt/pull/81))
- **deps-dev:** Bump ora from 6.3.1 to 7.0.1 ([#79](https://github.com/aklinker1/wxt/pull/79))
- **deps-dev:** Bump @types/node from 20.4.5 to 20.5.0 ([#78](https://github.com/aklinker1/wxt/pull/78))
- **deps-dev:** Bump tsup from 7.1.0 to 7.2.0 ([#80](https://github.com/aklinker1/wxt/pull/80))
- **deps-dev:** Bump @vitest/coverage-v8 from 0.32.4 to 0.34.1 ([#84](https://github.com/aklinker1/wxt/pull/84))
- **deps-dev:** Bump vitepress from 1.0.0-beta.5 to 1.0.0-rc.4 ([#85](https://github.com/aklinker1/wxt/pull/85))
- **deps-dev:** Bump vitest-mock-extended from 1.1.4 to 1.2.0 ([#87](https://github.com/aklinker1/wxt/pull/87))
- **deps-dev:** Bump lint-staged from 13.3.0 to 14.0.0 ([#89](https://github.com/aklinker1/wxt/pull/89))
- Fix remote code E2E test ([83e62a1](https://github.com/aklinker1/wxt/commit/83e62a1))
- Fix failing demo build ([b58a15e](https://github.com/aklinker1/wxt/commit/b58a15e))
- **deps-dev:** Bump vitest-mock-extended from 1.2.0 to 1.2.1 ([#97](https://github.com/aklinker1/wxt/pull/97))
- **deps-dev:** Bump lint-staged from 14.0.0 to 14.0.1 ([#100](https://github.com/aklinker1/wxt/pull/100))
- **deps-dev:** Bump vitest from 0.34.1 to 0.34.3 ([#99](https://github.com/aklinker1/wxt/pull/99))
- Increase E2E test timeout because GitHub Actions Window runner is slow ([2a0842b](https://github.com/aklinker1/wxt/commit/2a0842b))
- **deps-dev:** Bump vitepress from 1.0.0-rc.4 to 1.0.0-rc.10 ([#96](https://github.com/aklinker1/wxt/pull/96))
- Fix test watcher restarting indefinetly ([2c7922c](https://github.com/aklinker1/wxt/commit/2c7922c))
- Remove explict icon config from templates ([93bfee0](https://github.com/aklinker1/wxt/commit/93bfee0))
- Use import aliases in Vue template ([#104](https://github.com/aklinker1/wxt/pull/104))
#### ⚠️ Breaking Changes
- ⚠️ Use relative path aliases inside `.wxt/tsconfig.json` ([#102](https://github.com/aklinker1/wxt/pull/102))
### ❤️ Contributors
- Aaron <aaronklinker1@gmail.com>
- Aaron Klinker <aaronklinker1@gmail.com>
## v0.3.2
[compare changes](https://github.com/aklinker1/wxt/compare/v0.3.1...v0.3.2)
### 🚀 Enhancements
- Discover icons from the public directory ([#72](https://github.com/aklinker1/wxt/pull/72))
- Don't allow auto-importing from subdirectories ([d54d611](https://github.com/aklinker1/wxt/commit/d54d611))
### 📖 Documentation
- Document the `url:` import prefix for remote code ([323045a](https://github.com/aklinker1/wxt/commit/323045a))
- Fix typos ([97f0938](https://github.com/aklinker1/wxt/commit/97f0938))
- Fix capitalization ([39467d1](https://github.com/aklinker1/wxt/commit/39467d1))
- Generate markdown for config reference ([#74](https://github.com/aklinker1/wxt/pull/74))
### 🏡 Chore
- Upgrade dependencies ([798f02f](https://github.com/aklinker1/wxt/commit/798f02f))
- Upgrade vite (`v4.3` &rarr; `v4.4`) ([547c185](https://github.com/aklinker1/wxt/commit/547c185))
- Update templates to work with CSS entrypoints ([7f15305](https://github.com/aklinker1/wxt/commit/7f15305))
- Improve file list output in CI ([#73](https://github.com/aklinker1/wxt/pull/73))
### 🤖 CI
- Validate templates against `main` ([#66](https://github.com/aklinker1/wxt/pull/66))
- List vite version when validating project templates ([ef140dc](https://github.com/aklinker1/wxt/commit/ef140dc))
- Validate templates using tarball to avoid version conflicts within the `wxt/node_modules` directory ([edfa075](https://github.com/aklinker1/wxt/commit/edfa075))
### ❤️ Contributors
- Aaron <aaronklinker1@gmail.com>
- Aaron Klinker <aaronklinker1@gmail.com>
## v0.3.1
[compare changes](https://github.com/aklinker1/wxt/compare/v0.3.0...v0.3.1)
@@ -33,9 +121,8 @@
### ❤️ Contributors
- Aaron Klinker <aaronklinker1@gmail.com>
- Aaron
- BeanWei <bean0wei@dingtalk.com>
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
- BeanWei ([@BeanWei](https://github.com/BeanWei))
## v0.3.0
@@ -70,7 +157,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.5
@@ -96,7 +183,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.4
@@ -108,7 +195,7 @@
### ❤️ Contributors
- Aaron
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.3
@@ -122,7 +209,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.2
@@ -134,7 +221,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.1
@@ -150,7 +237,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.0
@@ -178,7 +265,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.6
@@ -196,8 +283,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.5
@@ -209,7 +295,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.4
@@ -230,7 +316,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.3
@@ -255,7 +341,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.2
@@ -277,7 +363,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.1
@@ -290,7 +376,7 @@
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.0
@@ -317,7 +403,7 @@ Initial release of WXT. Full support for production builds and initial toolkit f
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.0.2
@@ -351,7 +437,7 @@ Initial release of WXT. Full support for production builds and initial toolkit f
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.0.1
@@ -425,4 +511,4 @@ Initial release of WXT. Full support for production builds and initial toolkit f
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
+72
View File
@@ -0,0 +1,72 @@
# Contributing
Everyone is welcome to contribute to WXT!
If you are changing the docs or fixing a bug, feel free to fork and open a PR.
If you want to add a new feature, please create an issue or discussion first so we can decide if the feature is inline with the vision for WXT.
## Conventional Commits
This project uses [Conventional Commits](https://www.conventionalcommits.org/en) to automate versioning. If you're a new contributor, don't worry about this. When you open a PR, a maintainer will change the PR's title so it's in the style of conventional commits, but that's all.
Maintainers, commits to the `main` branch (either directly or via PRs) must be valid conventional commits.
## Setup
WXT uses `pnpm`, so make sure you have it installed.
```sh
corepack enable
```
Then, simply run the install command:
```sh
pnpm i
```
## Development
Here are some helpful commands:
```sh
# Build WXT package
pnpm build
```
```sh
# Build WXT package, then build demo extension
cd demo
pnpm build
```
```sh
# Build WXT package, then start the demo extension in dev mode
cd demo
pnpm dev
```
```sh
# Run unit and E2E tests
pnpm test
```
```sh
# Start the docs website locally
pnpm docs:dev
```
## Testing
WXT has unit and E2E tests. When making a change or adding a feature, make sure to update the tests or add new ones.
To run tests for a specific file, add the filename at the end of the test command:
```sh
pnpm test manifest-contents
```
Unit and E2E tests are ran together via [Vitest workspaces](https://vitest.dev/guide/#workspaces-support).
If you want to manually test a change, you can modify the demo project for your test, but please don't leave those changes committed once you open a PR.
+29
View File
@@ -0,0 +1,29 @@
{
"prompt_for_name": {
"message": "What's your name?",
"description": "Ask for the user's name"
},
"hello": {
"message": "Hello, $USER$",
"description": "Greet the user",
"placeholders": {
"user": {
"content": "$1",
"example": "Cira"
}
}
},
"bye": {
"message": "Goodbye, $USER$. Come back to $OUR_SITE$ soon!",
"description": "Say goodbye to the user",
"placeholders": {
"our_site": {
"content": "Example.com"
},
"user": {
"content": "$1",
"example": "Cira"
}
}
}
}
+7
View File
@@ -12,4 +12,11 @@ export default defineBackground(() => {
browser.runtime.getURL('/');
browser.runtime.getURL('/background.js');
browser.runtime.getURL('/icon/128.png');
// @ts-expect-error: should only accept known message names
browser.i18n.getMessage('test');
browser.i18n.getMessage('prompt_for_name');
browser.i18n.getMessage('hello', 'Aaron');
browser.i18n.getMessage('bye', ['Aaron']);
browser.i18n.getMessage('@@extension_id');
});
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -1,4 +1,4 @@
import 'url:https://stats.aklinker1.io/umami.js';
import 'url:https://code.jquery.com/jquery-3.7.1.slim.min.js';
console.log(browser.runtime.id);
logId();
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -1,3 +1,3 @@
{
"extends": ["../tsconfig.base.json", "./src/.wxt/tsconfig.json"]
"extends": ["../tsconfig.base.json", "./.wxt/tsconfig.json"]
}
+1 -7
View File
@@ -6,12 +6,6 @@ export default defineConfig({
chrome: '123',
},
manifest: {
icons: {
'16': 'icon/16.png',
'32': 'icon/32.png',
'48': 'icon/48.png',
'96': 'icon/96.png',
'128': 'icon/128.png',
},
default_locale: 'en',
},
});
+6
View File
@@ -1,8 +1,13 @@
import { defineConfig } from 'vitepress';
import { generateConfigDocs } from './plugins/generate-config-docs';
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: 'WXT',
vite: {
clearScreen: false,
plugins: [generateConfigDocs()],
},
description: 'Next gen framework for developing web extensions',
lastUpdated: true,
@@ -47,6 +52,7 @@ export default defineConfig({
{ text: 'Auto-imports', link: '/guide/auto-imports.md' },
{ text: 'Manifest.json', link: '/guide/manifest.md' },
{ text: 'Extension APIs', link: '/guide/extension-apis.md' },
{ text: 'Remote Code', link: '/guide/remote-code.md' },
],
},
{
@@ -0,0 +1,146 @@
import { resolve } from 'node:path';
import { Project, ts, Type, Node, JSDocableNode } from 'ts-morph';
import { readFileSync, writeFileSync } from 'node:fs';
import { format } from 'prettier';
import consola from 'consola';
let hasGenerated = false;
const externalTypesPath = resolve('src/core/types/external.ts');
const configTemplatePath = resolve('docs/config.tpl.md');
const configPath = resolve('docs/config.md');
const PREFACE = `<!--
DO NOT EDIT
Generated by \`wxt/docs/.vitepress/plugins/generate-config-docs.ts\`
To make changes to the config reference, update the JSDoc in \`src/core/types/external.ts\`.
-->`;
/**
* Custom property paths that should not be recursively inspected. Usually 3rd party types.
*/
const LEAF_PATHS = ['imports', 'vite', 'server'];
/**
* Override any types that resolve to `import(...)` instead of their type names when calling
* `type.getText()`
*/
const CUSTOM_TYPES = {
manifest:
'Manifest | Promise<Manifest> | () => Manifest | () => Promise<Manifest>',
};
export function generateConfigDocs() {
writeFileSync(configPath, '');
const generateDocs = async () => {
consola.info('Generating /config.md');
try {
const project = new Project({
tsConfigFilePath: resolve('tsconfig.json'),
});
// Load file containing "UserConfig"
const externalTypesFile = project.addSourceFileAtPath(externalTypesPath);
project.resolveSourceFileDependencies();
const inlineConfigInterface =
externalTypesFile.getInterfaceOrThrow('InlineConfig');
const getDocsFor = (path: string[], node: Node<ts.Node>): string[] => {
const pathStr = path.join('.');
let type: Type<ts.Type>;
if (node.isKind(ts.SyntaxKind.InterfaceDeclaration)) {
type = node.getType();
} else if (node.isKind(ts.SyntaxKind.PropertySignature)) {
type = node.getTypeNodeOrThrow()?.getType();
} else if (node.isKind(ts.SyntaxKind.MethodSignature)) {
type = node.getType();
} else {
throw Error('Unsupported type node: ' + node.getKindName());
}
if (type.isObject() && !type.isArray()) {
return (
type
.getProperties()
// .sort((l, r) => l.getName().localeCompare(r.getName()))
.flatMap((property) => {
const childPath = [...path, property.getName()];
if (LEAF_PATHS.includes(childPath.join('.'))) return [];
return getDocsFor(childPath, property.getDeclarations()[0]);
})
);
}
if ('getJsDocs' in node) {
const lines: string[] = [];
const docs = (node as unknown as JSDocableNode).getJsDocs();
let typeText: string;
if (CUSTOM_TYPES[pathStr]) {
typeText = CUSTOM_TYPES[pathStr];
} else if (type.isUnion() && !type.isBoolean()) {
typeText = type
.getUnionTypes()
.map((type) => type.getText())
.join(' | ');
} else {
typeText = type.getText();
}
const defaultValue = docs
.flatMap((doc) => doc.getTags())
.find((tag) => tag.getTagName() === 'default')
?.getCommentText();
lines.push(
'',
`## ${pathStr}`,
'',
`- **Type**: \`${typeText}\``,
`- **Default**: \`${defaultValue}\``,
...docs.flatMap((doc) => doc.getDescription()),
);
return lines;
}
return [];
};
const lines = getDocsFor([], inlineConfigInterface);
const text = await format(
PREFACE +
'\n\n' +
readFileSync(configTemplatePath, 'utf-8').replace(
'{{ DOCS }}',
lines.join('\n'),
),
{ parser: 'markdown' },
);
writeFileSync(configPath, text);
consola.success('Generated /config.md');
} catch (err) {
consola.fail('Failed to generate /config.md');
consola.error(err.message);
}
};
return {
name: 'docs:generate-config-docs',
async config() {
if (!hasGenerated) {
hasGenerated = true;
await generateDocs();
}
},
configureServer(server: any) {
server.watcher.add(externalTypesPath);
},
async handleHotUpdate(ctx: { file: string }) {
if ([externalTypesPath, configTemplatePath].includes(ctx.file)) {
await generateDocs();
}
},
};
}
+36 -17
View File
@@ -1,31 +1,46 @@
/* Colors */
:root {
--wxt-c-green: #53bc4a;
--wxt-c-green-light: #67d45e;
--wxt-c-green-lighter: #67d45e;
--wxt-c-green-dark: #4fa048;
--wxt-c-green-darker: #447e3f;
--wxt-c-green-1: #67d45e;
--wxt-c-green-2: #4fa048;
--wxt-c-green-3: #447e3f;
}
/* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */
:root {
--vp-c-brand: var(--wxt-c-green);
--vp-c-brand-light: var(--wxt-c-green-light);
--vp-c-brand-lighter: var(--wxt-c-green-lighter);
--vp-c-brand-dark: var(--wxt-c-green-dark);
--vp-c-brand-darker: var(--wxt-c-green-darker);
--vp-c-brand-1: var(--wxt-c-green-1);
--vp-c-brand-2: var(--wxt-c-green-2);
--vp-c-brand-3: var(--wxt-c-green-3);
--vp-button-brand-bg: var(--wxt-c-green);
--vp-button-brand-hover-bg: var(--wxt-c-green-2);
--vp-button-brand-active-bg: var(--wxt-c-green-3);
--vp-code-link-color: var(--wxt-c-green);
/* --vp-c-text-1: var(--wxt-c-green-1); */
--vp-button-brand-text: var(--vp-c-black);
--vp-button-brand-hover-text: var(--vp-c-black);
--vp-button-brand-active-text: var(--vp-c-black);
--vp-custom-block-tip-border: var(--wxt-c-green-dark);
--vp-custom-block-tip-text: var(--wxt-c-green-dark);
--vp-custom-block-tip-border: var(--wxt-c-green);
--vp-custom-block-tip-text: var(--wxt-c-green);
--vp-code-block-bg: #222422;
/* --vp-code-block-bg: #222422;
--vp-code-copy-code-bg: #313431;
--vp-code-copy-code-hover-bg: #3c403c;
--vp-code-copy-code-hover-bg: #3c403c; */
--vp-custom-block-tip-bg: var(--vp-code-block-bg);
--vp-custom-block-info-bg: var(--vp-code-block-bg);
--vp-code-color: #476582;
}
.vp-doc a {
color: var(--wxt-c-green);
}
.dark {
@@ -38,15 +53,19 @@
--vp-c-bg-alt: #171817;
--vp-c-mute: #313136;
--vp-c-mute-light: #3a3a3c;
--vp-c-mute-lighter: #505053;
--vp-c-mute-dark: #2c2c30;
--vp-c-mute-darker: #252529;
--vp-c-default: #313136;
--vp-c-default-1: #3a3a3c;
--vp-c-default-2: #505053;
--vp-c-default-3: #2c2c30;
--vp-c-default-soft: #252529;
--vp-code-block-bg: #191a19;
--vp-code-copy-code-bg: #212321;
--vp-code-copy-code-hover-bg: #292d29;
--vp-custom-block-info-bg: #191a19;
--vp-code-color: #c9def1;
}
.vp-doc .no-vertical-dividers th,
-5
View File
@@ -1,5 +0,0 @@
# Config
:::warning 🚧&ensp;Under construction
This documentation does not exist yet.
:::
+5
View File
@@ -0,0 +1,5 @@
# Config Reference
Discover all the options you can use in your `wxt.config.ts` file.
{{ DOCS }}
+1 -1
View File
@@ -2,7 +2,7 @@
WXT has two directories for storing assets like CSS, images, or fonts.
- `<srcDir>/public`: Store files that will be copied into the output directory as-is
- `<rootDir>/public`: Store files that will be copied into the output directory as-is
- `<srcDir>/assets`: Store files that will be processed by Vite during the build process
## `/public` Directory
+1 -1
View File
@@ -24,7 +24,7 @@ Lets compare the features of WXT vs [Plasmo](https://docs.plasmo.com/framework),
| Opens browser and install extension | ✅ | ❌ |
| HMR for UIs | ✅ | 🟡 React only |
| Reload HTML Files on Change | ✅ | 🟡 Reloads entire extension |
| Reload Content Scripta on Change | ✅ | 🟡 Reloads entire extension |
| Reload Content Scripts on Change | ✅ | 🟡 Reloads entire extension |
| Reload Background on Change | 🟡 Reloads entire extension | 🟡 Reloads entire extension |
| <strong style="opacity: 50%">Built-in Utils</strong> | | |
| Storage | ❌ | ✅ |
+1 -1
View File
@@ -25,7 +25,7 @@ WXT allows you to edit several directories to your liking:
- `root` (default: `process.cwd()`) - Root of the WXT project
- `srcDir` (default: `<rootDir>`) - Location of all your source code
- `entrypointsDir` (default: `<srcDir>/entrypoints`) - Folder containing all the entrypoints.
- `publicDir` (default: `<srcDir>/public`) - Folder containing [public assets](/get-started/assets.md)
- `publicDir` (default: `<rootDir>/public`) - Folder containing [public assets](/get-started/assets.md)
### Example
+2 -2
View File
@@ -1,8 +1,8 @@
# Defining Entrypoints
An entrypoints is any HTML, JS, or CSS file that needs to be bundled and included with the extension.
An "entrypoint" is any HTML, JS, or CSS file that needs to be bundled and included with the extension.
They may or may not be listed in the extension's `manifest.json`.
Entrypoints may or may not be listed in the extension's `manifest.json`.
## `/entrypoints` Directory
+1 -1
View File
@@ -2,7 +2,7 @@
WXT is a free and open source framework for building web extensions in an conventional, intuative, and safe way **_for all browsers_**.
WXT comes with full TypeScript support and auto-imports. Sounds familiar? That's right, **_WXT was based off of Nuxt_** and aims to provide the same greate DX and features.
WXT is based of [Nuxt](https://nuxt.com), and aims to provide the same great DX with TypeScript, auto-imports, and an opinionated project structure.
![Example build output](../assets/cli-output.png)
+13 -4
View File
@@ -20,10 +20,19 @@ And more. All [`wxt/client`](/config.md#wxtclient) APIs can be used without impo
In addition WXT APIs, default and named exports from inside the following directories can be used without listing them in imports.
- `<srcDir>/components/**/*`
- `<srcDir>/composables/**/*`
- `<srcDir>/hooks/**/*`
- `<srcDir>/utils/**/*`
- `<srcDir>/components/*`
- `<srcDir>/composables/*`
- `<srcDir>/hooks/*`
- `<srcDir>/utils/*`
To add auto-imports from subdirectories, like `utils/api/some-file.ts`, re-export them from the base directory:
```ts
// utils/index.ts
export * from './api/some-file.ts';
```
Alternatively, you could add the directory to the list of auto-import directories in your config file.
## TypeScript
+1 -1
View File
@@ -16,7 +16,7 @@
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+5 -5
View File
@@ -2,16 +2,16 @@
[Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/content_scripts/) &bull; [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts)
When creating content script entrypoints, they are automatically included in the `manifest.json` along with any CSS they import.
When creating content script entrypoints, they are automatically included in the `manifest.json` along with any CSS files they import.
## Filenames
<EntrypointPatterns
:patterns="[
['content.tsx?', 'content-scripts/content.js'],
['content/index.tsx?', 'content-scripts/content.js'],
['<name>.content.tsx?', 'content-scripts/<name>.js'],
['<name>.content/index.tsx?', 'content-scripts/<name>.js'],
['content.(ts|tsx)', 'content-scripts/content.js'],
['content/index.(ts|tsx)', 'content-scripts/content.js'],
['<name>.content.(ts|tsx)', 'content-scripts/<name>.js'],
['<name>.content/index.(ts|tsx)', 'content-scripts/<name>.js'],
]"
/>
+1 -1
View File
@@ -16,7 +16,7 @@
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+10 -8
View File
@@ -14,9 +14,11 @@ And that's it! Your extension now supports Chrome, Firefox, Safari, Edge, and ot
The `browser` variable is available globally via [auto-imports](/guide/auto-imports.md), or it can be imported manually.
```ts
import browser from 'webextension-polyfill';
import browser from 'wxt/browser';
```
The `wxt/browser` module exports a customized version of `webextension-polyfill`'s browser with improved typing.
### Example
Let's save the date the extension was installed. Just like `chrome`, some APIs require the permission is added to your manifest before the API is defined. Here, we need to add the `storage` permission to your manifest.
@@ -51,6 +53,13 @@ Follow [Chrome's message passing guide](https://developer.chrome.com/docs/extens
Here's a basic request/response example:
```ts
// popup/main.ts
const res = await browser.runtime.sendMessage('ping');
console.log('res'); // "pong"
```
```ts
// background.ts
export default defineBackground(() => {
@@ -66,13 +75,6 @@ export default defineBackground(() => {
});
```
```ts
// popup/main.ts
const res = await browser.runtime.sendMessage('ping');
console.log('res'); // "pong"
```
There are a number of message passing libraries you can use to improve the message passing experience.
Here are some that are compatible with WXT (because they are based off `webextension-polyfill` as well):
+1 -1
View File
@@ -16,7 +16,7 @@
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+16 -10
View File
@@ -2,7 +2,7 @@
The manifest.json is generated at build-time based on files in your `entrypoints` directory and your `wxt.config.ts`.
## Customization
## Confiuration
While entrypoints are generated and added to the manifest at build-time, you can customize or add to your `manifest.json` in the config file.
@@ -47,9 +47,9 @@ The [manifest's `version` and `version_name`](https://developer.chrome.com/docs/
}
```
### `icons`
## `icons`
The [manifest's `icons`](https://developer.chrome.com/docs/extensions/mv3/manifest/icons/) property needs to be set in the config file. The files should be added to WXT's [`public` directory](/get-started/assets#public-directory).
By default, WXT will discover icons in your [`public` directory](/get-started/assets#public-directory) and use them for the [manifest's `icons`](https://developer.chrome.com/docs/extensions/mv3/manifest/icons/).
```
public/
@@ -60,21 +60,27 @@ public/
└─ icon-128.png
```
Icon files need to match the following regex to be automatically included in the manifest. Most design software can output icons in one of these formats
<<< @/../src/core/utils/manifest.ts#snippet
If you prefer to use filenames in a different format, you can add the icons manually in your `wxt.config.ts` file:
```ts
export default defineConfig({
manifest: {
icons: {
16: '/icon-16.png',
24: '/icon-24.png',
48: '/icon-48.png',
96: '/icon-96.png',
128: '/icon-128.png',
16: '/extension-icon-16.png',
24: '/extension-icon-24.png',
48: '/extension-icon-48.png',
96: '/extension-icon-96.png',
128: '/extension-icon-128.png',
},
},
});
```
### Permissions
## `permissions`
[Permissions](https://developer.chrome.com/docs/extensions/reference/permissions/) must be listed in the manifest config.
@@ -86,7 +92,7 @@ export default defineConfig({
});
```
### Localization
## Localization
Similar to the icon, the [`_locales` directory](https://developer.chrome.com/docs/extensions/reference/i18n/) should be placed inside the the WXT's [`public` directory](/get-started/assets#public-directory).
+1 -1
View File
@@ -16,7 +16,7 @@
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -3
View File
@@ -13,10 +13,8 @@
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -3
View File
@@ -13,10 +13,8 @@
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+31
View File
@@ -0,0 +1,31 @@
# Remote Code
WXT will automatically download and bundle imports with the `url:` prefix so the extension does not depend of remote code, [a requirement from Google for MV3](https://developer.chrome.com/docs/extensions/migrating/improve-security/#remove-remote-code).
## Google Analytics
For example, you can import google analytics:
```ts
// utils/google-analytics.ts
import 'url:https://www.googletagmanager.com/gtag/js?id=G-XXXXXX';
window.dataLayer = window.dataLayer || [];
// NOTE: This line is different from Google's documentation
window.gtag = function () {
dataLayer.push(arguments);
};
gtag('js', new Date());
gtag('config', 'G-XXXXXX');
```
Then you can import this in your HTML files to enable Google Analytics:
```ts
// popup/main.ts
import '~/utils/google-analytics';
gtag('event', 'event_name', {
key: 'value',
});
```
+1 -1
View File
@@ -22,7 +22,7 @@ Firefox does not support sandboxed pages.
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -22,7 +22,7 @@ Firefox does not support sidepanel pages.
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -32,7 +32,7 @@ console.log(url); // "chrome-extension://<id>/<name>.html"
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+2 -2
View File
@@ -6,8 +6,8 @@ TypeScript files that are built, but are not included in the manifest.
<EntrypointPatterns
:patterns="[
['<name>.tsx?', '<name>.js'],
['<name>/index.tsx?', '<name>.js'],
['<name>.(ts|tsx)', '<name>.js'],
['<name>/index.(ts|tsx)', '<name>.js'],
]"
/>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

After

Width:  |  Height:  |  Size: 472 KiB

+1 -1
View File
@@ -28,7 +28,7 @@ describe('Auto Imports', () => {
| \\"/background.js\\"
| \\"/content-scripts/overlay.js\\"
| \\"/popup.html\\"
export interface ProjectRuntime extends Runtime.Static {
export interface WxtRuntime extends Runtime.Static {
getURL(path: PublicPath): string;
}
}
+100
View File
@@ -133,6 +133,66 @@ describe('Manifest Content', () => {
});
});
describe('icons', () => {
it('should auto-discover icons with the correct name', async () => {
const project = new TestProject();
project.addFile('public/icon-16.png');
project.addFile('public/icon/32.jpeg');
project.addFile('public/icon@48w.jpg');
project.addFile('public/icon-64x64.gif');
project.addFile('public/icon@96.bmp');
project.addFile('public/icon/128x128.ico');
await project.build();
const manifest = await project.getOutputManifest();
expect(manifest.icons).toEqual({
'16': 'icon-16.png',
'32': 'icon/32.jpeg',
'48': 'icon@48w.jpg',
'64': 'icon-64x64.gif',
'96': 'icon@96.bmp',
'128': 'icon/128x128.ico',
});
});
it('should return undefined when no icons are found', async () => {
const project = new TestProject();
project.addFile('public/logo.png');
project.addFile('public/icon.jpeg');
await project.build();
const manifest = await project.getOutputManifest();
expect(manifest.icons).toBeUndefined();
});
it('should allow icons to be overwritten from the wxt.config.ts file', async () => {
const project = new TestProject();
project.addFile('public/icon-16.png');
project.addFile('public/icon-32.png');
project.addFile('public/logo-16.png');
project.addFile('public/logo-32.png');
project.addFile('public/logo-48.png');
const icons = {
'16': 'logo-16.png',
'32': 'logo-32.png',
'48': 'logo-48.png',
};
project.setConfigFileConfig({
manifest: {
icons,
},
});
await project.build();
const manifest = await project.getOutputManifest();
expect(manifest.icons).toEqual(icons);
});
});
it('should group content scripts and styles together based on their matches and run_at', async () => {
const project = new TestProject();
project.addFile(
@@ -206,4 +266,44 @@ describe('Manifest Content', () => {
js: ['content-scripts/one.js'],
});
});
it('should add to any content scripts declared in wxt.config.ts', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/one.content/index.ts',
`export default defineContentScript({
matches: ["*://google.com/*"],
main: () => {},
})`,
);
project.addFile(
'entrypoints/two.content/style.css',
`body {
background-color: red;
}`,
);
project.setConfigFileConfig({
manifest: {
content_scripts: [
{
css: ['content-scripts/two.css'],
matches: ['*://*.google.com/*'],
},
],
},
});
await project.build();
const manifest = await project.getOutputManifest();
expect(manifest.content_scripts).toContainEqual({
css: ['content-scripts/two.css'],
matches: ['*://*.google.com/*'],
});
expect(manifest.content_scripts).toContainEqual({
matches: ['*://google.com/*'],
js: ['content-scripts/one.js'],
});
});
});
+2 -2
View File
@@ -3,7 +3,7 @@ import { TestProject } from '../utils';
describe('Remote Code', () => {
it('should download "url:*" modules and include them in the final bundle', async () => {
const url = 'https://stats.aklinker1.io/umami.js';
const url = 'https://code.jquery.com/jquery-3.7.1.slim.min.js';
const project = new TestProject();
project.addFile('entrypoints/popup.ts', `import "url:${url}"`);
@@ -12,7 +12,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
'umami.disabled',
'jQuery v3.7.1',
);
expect(output).not.toContain(url);
expect(
+341
View File
@@ -0,0 +1,341 @@
import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
describe('TypeScript Project', () => {
it('should generate defined constants correctly', async () => {
const project = new TestProject();
await project.build();
const output = await project.serializeFile('.wxt/types/globals.d.ts');
expect(output).toMatchInlineSnapshot(`
".wxt/types/globals.d.ts
----------------------------------------
// Generated by wxt
export {}
declare global {
const __MANIFEST_VERSION__: 2 | 3;
const __BROWSER__: string;
const __IS_CHROME__: boolean;
const __IS_FIREFOX__: boolean;
const __IS_SAFARI__: boolean;
const __IS_EDGE__: boolean;
const __IS_OPERA__: boolean;
const __COMMAND__: \\"build\\" | \\"serve\\";
}
"
`);
});
it('should define auto-import globals', async () => {
const project = new TestProject();
await project.build();
const output = await project.serializeFile('.wxt/types/imports.d.ts');
expect(output).toMatchInlineSnapshot(`
".wxt/types/imports.d.ts
----------------------------------------
// Generated by wxt
export {}
declare global {
const browser: typeof import('wxt/browser')['browser']
const defineBackground: typeof import('wxt/client')['defineBackground']
const defineConfig: typeof import('wxt')['defineConfig']
const defineContentScript: typeof import('wxt/client')['defineContentScript']
const mountContentScriptUi: typeof import('wxt/client')['mountContentScriptUi']
}
"
`);
});
it('should augment the types for browser.runtime.getURL', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html');
project.addFile('entrypoints/options.html');
project.addFile('entrypoints/sandbox.html');
await project.build();
const output = await project.serializeFile('.wxt/types/paths.d.ts');
expect(output).toMatchInlineSnapshot(`
".wxt/types/paths.d.ts
----------------------------------------
// Generated by wxt
import \\"wxt/browser\\";
declare module \\"wxt/browser\\" {
type PublicPath =
| \\"/options.html\\"
| \\"/popup.html\\"
| \\"/sandbox.html\\"
export interface WxtRuntime extends Runtime.Static {
getURL(path: PublicPath): string;
}
}
"
`);
});
it('should augment the types for browser.i18n.getMessage', async () => {
const project = new TestProject();
project.addFile(
'public/_locales/en/messages.json',
JSON.stringify({
prompt_for_name: {
message: "What's your name?",
description: "Ask for the user's name",
},
hello: {
message: 'Hello, $USER$',
description: 'Greet the user',
placeholders: {
user: {
content: '$1',
example: 'Cira',
},
},
},
bye: {
message: 'Goodbye, $USER$. Come back to $OUR_SITE$ soon!',
description: 'Say goodbye to the user',
placeholders: {
our_site: {
content: 'Example.com',
},
user: {
content: '$1',
example: 'Cira',
},
},
},
}),
);
project.setConfigFileConfig({
manifest: {
default_locale: 'en',
},
});
await project.build();
const output = await project.serializeFile('.wxt/types/i18n.d.ts');
expect(output).toMatchInlineSnapshot(`
".wxt/types/i18n.d.ts
----------------------------------------
// Generated by wxt
import \\"wxt/browser\\";
declare module \\"wxt/browser\\" {
/**
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
*/
interface GetMessageOptions {
/**
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
*/
escapeLt?: boolean
}
export interface WxtI18n extends I18n.Static {
/**
* The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even unlocalized extensions can use this message.
Note: You can't use this message in a manifest file.
*
* \\"<browser.runtime.id>\\"
*/
getMessage(
messageName: \\"@@extension_id\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
*
*
* \\"<browser.i18n.getUiLocale()>\\"
*/
getMessage(
messageName: \\"@@ui_locale\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* The text direction for the current locale, either \\"ltr\\" for left-to-right languages such as English or \\"rtl\\" for right-to-left languages such as Japanese.
*
* \\"<ltr|rtl>\\"
*/
getMessage(
messageName: \\"@@bidi_dir\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* If the @@bidi_dir is \\"ltr\\", then this is \\"rtl\\"; otherwise, it's \\"ltr\\".
*
* \\"<rtl|ltr>\\"
*/
getMessage(
messageName: \\"@@bidi_reversed_dir\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* If the @@bidi_dir is \\"ltr\\", then this is \\"left\\"; otherwise, it's \\"right\\".
*
* \\"<left|right>\\"
*/
getMessage(
messageName: \\"@@bidi_start_edge\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* If the @@bidi_dir is \\"ltr\\", then this is \\"right\\"; otherwise, it's \\"left\\".
*
* \\"<right|left>\\"
*/
getMessage(
messageName: \\"@@bidi_end_edge\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* Ask for the user's name
*
* \\"What's your name?\\"
*/
getMessage(
messageName: \\"prompt_for_name\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* Greet the user
*
* \\"Hello, $USER$\\"
*/
getMessage(
messageName: \\"hello\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
/**
* Say goodbye to the user
*
* \\"Goodbye, $USER$. Come back to $OUR_SITE$ soon!\\"
*/
getMessage(
messageName: \\"bye\\",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;
}
}
"
`);
});
it('should reference all the required types in a single declaration file', async () => {
const project = new TestProject();
await project.build();
const output = await project.serializeFile('.wxt/wxt.d.ts');
expect(output).toMatchInlineSnapshot(`
".wxt/wxt.d.ts
----------------------------------------
// Generated by wxt
/// <reference types=\\"vite/client\\" />
/// <reference types=\\"./types/imports.d.ts\\" />
/// <reference types=\\"./types/paths.d.ts\\" />
/// <reference types=\\"./types/i18n.d.ts\\" />
/// <reference types=\\"./types/globals.d.ts\\" />
"
`);
});
it('should generate a TSConfig file for the project', async () => {
const project = new TestProject();
await project.build();
const output = await project.serializeFile('.wxt/tsconfig.json');
expect(output).toMatchInlineSnapshot(`
".wxt/tsconfig.json
----------------------------------------
{
\\"compilerOptions\\": {
\\"target\\": \\"ESNext\\",
\\"module\\": \\"ESNext\\",
\\"moduleResolution\\": \\"Bundler\\",
\\"noEmit\\": true,
\\"esModuleInterop\\": true,
\\"forceConsistentCasingInFileNames\\": true,
\\"resolveJsonModule\\": true,
\\"strict\\": true,
\\"lib\\": [\\"DOM\\", \\"WebWorker\\"],
\\"skipLibCheck\\": true,
\\"paths\\": {
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"],
\\"@\\": [\\"..\\"],
\\"@/*\\": [\\"../*\\"],
\\"~\\": [\\"..\\"],
\\"~/*\\": [\\"../*\\"]
}
},
\\"include\\": [
\\"../**/*\\",
\\"./wxt.d.ts\\"
],
\\"exclude\\": [\\"../.output\\"]
}"
`);
});
it('should correct path aliases for a custom srcDir', async () => {
const project = new TestProject();
project.setConfigFileConfig({
srcDir: 'src',
});
await project.build();
const output = await project.serializeFile('.wxt/tsconfig.json');
expect(output).toMatchInlineSnapshot(`
".wxt/tsconfig.json
----------------------------------------
{
\\"compilerOptions\\": {
\\"target\\": \\"ESNext\\",
\\"module\\": \\"ESNext\\",
\\"moduleResolution\\": \\"Bundler\\",
\\"noEmit\\": true,
\\"esModuleInterop\\": true,
\\"forceConsistentCasingInFileNames\\": true,
\\"resolveJsonModule\\": true,
\\"strict\\": true,
\\"lib\\": [\\"DOM\\", \\"WebWorker\\"],
\\"skipLibCheck\\": true,
\\"paths\\": {
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"],
\\"@\\": [\\"../src\\"],
\\"@/*\\": [\\"../src/*\\"],
\\"~\\": [\\"../src\\"],
\\"~/*\\": [\\"../src/*\\"]
}
},
\\"include\\": [
\\"../**/*\\",
\\"./wxt.d.ts\\"
],
\\"exclude\\": [\\"../.output\\"]
}"
`);
});
});
+2 -2
View File
@@ -10,8 +10,8 @@ export class TestProject {
config: UserConfig | undefined;
readonly root: string;
constructor(root = 'e2e/project') {
// We can't put each test's project inside e2e/project directly, otherwise the wxt.config.ts
constructor(root = 'e2e/dist') {
// We can't put each test's project inside e2e/dist directly, otherwise the wxt.config.ts
// file is cached and cannot be different between each test. Instead, we add a random ID to the
// end to make each test's path unique.
const id = Math.random().toString(32).substring(3);
+26 -25
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.3.1",
"version": "0.4.0",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18.16.0",
@@ -63,28 +63,27 @@
"docs:preview": "vitepress preview docs"
},
"dependencies": {
"@types/webextension-polyfill": "^0.10.0",
"@types/webextension-polyfill": "^0.10.1",
"@webext-core/fake-browser": "^1.2.2",
"@webext-core/match-patterns": "^1.0.1",
"async-mutex": "^0.4.0",
"c12": "^1.4.2",
"cac": "^6.7.14",
"consola": "^3.1.0",
"fast-glob": "^3.2.12",
"filesize": "^10.0.7",
"consola": "^3.2.3",
"fast-glob": "^3.3.1",
"filesize": "^10.0.8",
"fs-extra": "^11.1.1",
"get-port": "^7.0.0",
"giget": "^1.1.2",
"jiti": "^1.18.2",
"jiti": "^1.19.1",
"json5": "^2.2.3",
"linkedom": "^0.14.26",
"linkedom": "^0.15.1",
"minimatch": "^9.0.3",
"picocolors": "^1.0.0",
"prompts": "^2.4.2",
"unimport": "^3.0.8",
"vite": "^4.3.9",
"vite-tsconfig-paths": "^4.2.0",
"web-ext": "^7.6.2",
"unimport": "^3.1.0",
"vite": "^4.4.7",
"web-ext-run": "^0.1.0",
"webextension-polyfill": "^0.10.0",
"zip-dir": "^2.0.0"
},
@@ -92,21 +91,23 @@
"@faker-js/faker": "^8.0.2",
"@types/fs-extra": "^11.0.1",
"@types/lodash.merge": "^4.6.7",
"@types/node": "^20.3.1",
"@types/node": "^20.4.5",
"@types/prettier": "^3.0.0",
"@types/prompts": "^2.4.4",
"@vitest/coverage-v8": "^0.32.2",
"@vitest/coverage-v8": "^0.34.1",
"execa": "^7.2.0",
"lint-staged": "^14.0.0",
"lodash.merge": "^4.6.2",
"npm-run-all": "^4.1.5",
"ora": "^6.3.1",
"prettier": "^2.8.8",
"pretty-quick": "^3.1.3",
"simple-git-hooks": "^2.8.1",
"tsup": "^7.0.0",
"ora": "^7.0.1",
"prettier": "^3.0.1",
"simple-git-hooks": "^2.9.0",
"ts-morph": "^19.0.0",
"tsup": "^7.1.0",
"tsx": "^3.12.7",
"typescript": "^5.1.3",
"vitepress": "1.0.0-beta.5",
"vitest": "^0.32.4",
"typescript": "^5.1.6",
"vitepress": "1.0.0-rc.10",
"vitest": "^0.34.1",
"vitest-mock-extended": "^1.1.4",
"webextension-polyfill": "^0.10.0"
},
@@ -115,16 +116,16 @@
},
"packageManager": "pnpm@8.6.3",
"simple-git-hooks": {
"pre-commit": "pnpm pretty-quick --staged"
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "prettier --write"
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@algolia/client-search",
"search-insights"
],
"allowAny": [
"node-fetch"
]
}
}
+513 -1922
View File
File diff suppressed because it is too large Load Diff
+14 -4
View File
@@ -23,12 +23,14 @@ await Promise.all([
sourcemap: true,
dts: true,
silent: true,
external: ['vite'],
}),
tsup.build({
entry: { cli: 'src/cli/index.ts' },
format: ['cjs'],
sourcemap: 'inline',
silent: true,
external: ['vite'],
}),
tsup.build({
entry: { client: 'src/client/index.ts' },
@@ -36,6 +38,7 @@ await Promise.all([
sourcemap: 'inline',
dts: true,
silent: true,
external: ['vite'],
}),
tsup.build({
entry: { browser: 'src/client/browser.ts' },
@@ -43,6 +46,7 @@ await Promise.all([
sourcemap: 'inline',
dts: true,
silent: true,
external: ['vite'],
}),
...virtualEntrypoints.map((entryName) =>
tsup.build({
@@ -52,7 +56,7 @@ await Promise.all([
format: ['esm'],
sourcemap: true,
silent: true,
external: [`virtual:user-${entryName}`],
external: [`virtual:user-${entryName}`, 'vite'],
}),
),
tsup.build({
@@ -62,6 +66,7 @@ await Promise.all([
format: ['esm'],
sourcemap: true,
silent: true,
external: ['vite'],
}),
tsup.build({
entry: {
@@ -69,6 +74,7 @@ await Promise.all([
},
format: ['esm', 'cjs'],
silent: true,
external: ['vite'],
}),
]).catch((err) => {
spinner.fail();
@@ -76,9 +82,13 @@ await Promise.all([
process.exit(1);
});
spinner.succeed();
spinner.clear().stop();
const duration = Date.now() - startTime;
const outFiles = await glob(`${outDir}/**`, { absolute: true });
await printFileList(consola.log, outDir, outFiles);
consola.success(`Finished in ${formatDuration(duration)}`);
await printFileList(
consola.success,
`Built WXT in ${formatDuration(duration)}`,
outDir,
outFiles,
);
+2 -2
View File
@@ -1,4 +1,4 @@
declare module 'web-ext' {
declare module 'web-ext-run' {
export interface WebExtRunInstance {
reloadAllExtensions(): Promise<void>;
exit(): Promise<void>;
@@ -12,7 +12,7 @@ declare module 'web-ext' {
export default webExt;
}
declare module 'web-ext/util/logger' {
declare module 'web-ext-run/util/logger' {
// https://github.com/mozilla/web-ext/blob/e37e60a2738478f512f1255c537133321f301771/src/util/logger.js#L43
export interface IConsoleStream {
stopCapturing(): void;
+2
View File
@@ -10,6 +10,7 @@ export const build = defineCommand<
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
@@ -20,6 +21,7 @@ export const build = defineCommand<
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
await wxt.build(cliConfig);
+2
View File
@@ -10,6 +10,7 @@ export const dev = defineCommand<
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
@@ -20,6 +21,7 @@ export const dev = defineCommand<
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
const server = await wxt.createServer(cliConfig);
+4 -1
View File
@@ -9,7 +9,10 @@ import pc from 'picocolors';
import { Formatter } from 'picocolors/types';
export const init = defineCommand<
[directory: string | undefined, options: { template?: string; pm?: string }]
[
directory: string | undefined,
options: { template?: string; pm?: string; debug?: boolean },
]
>(
async (userDirectory, flags) => {
consola.info('Initalizing new project');
+2
View File
@@ -9,12 +9,14 @@ export const prepare = defineCommand<
root: string | undefined,
flags: {
config?: string;
debug?: boolean;
},
]
>(async (root, flags) => {
const cliConfig: wxt.InlineConfig = {
root,
configFile: flags.config,
debug: flags.debug,
};
const config = await getInternalConfig(cliConfig, 'build');
+4 -1
View File
@@ -2,7 +2,10 @@ import { consola } from 'consola';
import { defineCommand } from '../utils/defineCommand';
export const publish = defineCommand(
async (root: any, { config: configFile }: any) => {
async (
root: any,
{ config: configFile, debug }: { config?: string; debug?: string },
) => {
consola.warn('wxt publish: Not implemented');
},
);
+2
View File
@@ -13,6 +13,7 @@ export const zip = defineCommand<
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
@@ -23,6 +24,7 @@ export const zip = defineCommand<
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
const config = await getInternalConfig(cliConfig, 'build');
+2
View File
@@ -6,6 +6,8 @@ const cli = cac('wxt');
cli.help();
cli.version(version);
cli.option('--debug', 'enable debug mode');
// DEV
cli
.command('[root]', 'start dev server')
+8 -1
View File
@@ -1,4 +1,4 @@
import { consola } from 'consola';
import { LogLevels, consola } from 'consola';
import { printHeader } from '../../core/log/printHeader';
import { formatDuration } from '../../core/utils/formatDuration';
@@ -9,6 +9,13 @@ export function defineCommand<TArgs extends any[]>(
},
) {
return async (...args: TArgs) => {
// Enable consola's debug mode globally at the start of all commands when the `--debug` flag is
// passed
const isDebug = !!args.find((arg) => arg?.debug);
if (isDebug) {
consola.level = LogLevels.debug;
}
const startTime = Date.now();
try {
printHeader();
+8 -3
View File
@@ -1,10 +1,15 @@
import originalBrowser, { Browser, Runtime } from 'webextension-polyfill';
import originalBrowser, { Browser, Runtime, I18n } from 'webextension-polyfill';
export interface AugmentedBrowser extends Browser {
runtime: ProjectRuntime;
runtime: WxtRuntime;
i18n: WxtI18n;
}
export interface ProjectRuntime extends Runtime.Static {
export interface WxtRuntime extends Runtime.Static {
// Overriden per-project
}
export interface WxtI18n extends I18n.Static {
// Overriden per-project
}
+4 -2
View File
@@ -42,10 +42,12 @@ export async function buildInternal(
const { output } = await rebuild(config, groups, undefined);
// Post-build
config.logger.success(
await printBuildSummary(
config.logger.success,
`Built extension in ${formatDuration(Date.now() - startTime)}`,
output,
config,
);
await printBuildSummary(output, config);
return output;
}
+73 -10
View File
@@ -7,6 +7,8 @@ import { getUnimportOptions } from '../utils/auto-imports';
import { getGlobals } from '../utils/globals';
import { getPublicFiles } from '../utils/public';
import { normalizePath } from '../utils/paths';
import path from 'node:path';
import { Message, parseI18nMessages } from '../utils/i18n';
/**
* Generate and write all the files inside the `InternalConfig.typesDir` directory.
@@ -20,6 +22,7 @@ export async function generateTypesDir(
const references: string[] = [];
references.push(await writeImportsDeclarationFile(config));
references.push(await writePathsDeclarationFile(entrypoints, config));
references.push(await writeI18nDeclarationFile(config));
references.push(await writeGlobalsDeclarationFile(config));
const mainReference = await writeMainDeclarationFile(references, config);
@@ -70,7 +73,7 @@ import "wxt/browser";
declare module "wxt/browser" {
type PublicPath =
{{ union }}
export interface ProjectRuntime extends Runtime.Static {
export interface WxtRuntime extends Runtime.Static {
getURL(path: PublicPath): string;
}
}
@@ -84,6 +87,65 @@ declare module "wxt/browser" {
return filePath;
}
async function writeI18nDeclarationFile(
config: InternalConfig,
): Promise<string> {
const filePath = resolve(config.typesDir, 'i18n.d.ts');
const defaultLocale = config.manifest.default_locale;
const template = `// Generated by wxt
import "wxt/browser";
declare module "wxt/browser" {
/**
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
*/
interface GetMessageOptions {
/**
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
*/
escapeLt?: boolean
}
export interface WxtI18n extends I18n.Static {
{{ overrides }}
}
}
`;
let messages: Message[];
if (defaultLocale) {
const defaultLocalePath = path.resolve(
config.publicDir,
'_locales',
defaultLocale,
'messages.json',
);
const content = JSON.parse(await fs.readFile(defaultLocalePath, 'utf-8'));
messages = parseI18nMessages(content);
} else {
messages = parseI18nMessages({});
}
const overrides = messages.map((message) => {
return ` /**
* ${message.description ?? 'No message description.'}
*
* "${message.message}"
*/
getMessage(
messageName: "${message.name}",
substitutions?: string | string[],
options?: GetMessageOptions,
): string;`;
});
await fs.writeFile(
filePath,
template.replace('{{ overrides }}', overrides.join('\n')),
);
return filePath;
}
async function writeGlobalsDeclarationFile(
config: InternalConfig,
): Promise<string> {
@@ -128,6 +190,8 @@ async function writeTsConfigFile(
config: InternalConfig,
) {
const dir = config.wxtDir;
const rootPath = normalizePath(relative(dir, config.root));
const srcPath = normalizePath(relative(dir, config.srcDir));
await fs.writeFile(
resolve(dir, 'tsconfig.json'),
`{
@@ -142,16 +206,15 @@ async function writeTsConfigFile(
"strict": true,
"lib": ["DOM", "WebWorker"],
"skipLibCheck": true,
"baseUrl": "${normalizePath(relative(dir, config.root))}",
"paths": {
"@@": ["."],
"@@/*": ["./*"],
"~~": ["."],
"~~/*": ["./*"],
"@": ["${normalizePath(relative(config.root, config.srcDir))}"],
"@/*": ["${normalizePath(relative(config.root, config.srcDir))}/*"],
"~": ["${normalizePath(relative(config.root, config.srcDir))}"],
"~/*": ["${normalizePath(relative(config.root, config.srcDir))}/*"]
"@@": ["${rootPath}"],
"@@/*": ["${rootPath}/*"],
"~~": ["${rootPath}"],
"~~/*": ["${rootPath}/*"],
"@": ["${srcPath}"],
"@/*": ["${srcPath}/*"],
"~": ["${srcPath}"],
"~/*": ["${srcPath}/*"]
}
},
"include": [
+3 -1
View File
@@ -3,6 +3,8 @@ import { BuildOutput, InternalConfig } from '../types';
import { printFileList } from './printFileList';
export async function printBuildSummary(
log: (...args: any[]) => void,
header: string,
output: BuildOutput,
config: InternalConfig,
) {
@@ -18,7 +20,7 @@ export async function printBuildSummary(
});
const files = chunks.map((chunk) => resolve(config.outDir, chunk.fileName));
await printFileList(config.logger.log, config.outDir, files);
await printFileList(log, header, config.outDir, files);
}
const DEFAULT_SORT_WEIGHT = 100;
+3 -2
View File
@@ -6,6 +6,7 @@ import { printTable } from './printTable';
export async function printFileList(
log: (message: string) => void,
header: string,
baseDir: string,
files: string[],
): Promise<void> {
@@ -29,9 +30,9 @@ export async function printFileList(
}),
);
printTable(log, fileRows);
fileRows.push([`${pc.cyan('Σ Total size:')} ${String(filesize(totalSize))}`]);
log(`${pc.cyan('Σ Total size:')} ${String(filesize(totalSize))}`);
printTable(log, header, fileRows);
}
const DEFAULT_COLOR = pc.blue;
+2 -1
View File
@@ -1,5 +1,6 @@
export function printTable(
log: (message: string) => void,
header: string,
rows: string[][],
gap = 2,
): void {
@@ -24,5 +25,5 @@ export function printTable(
if (i !== rows.length - 1) str += '\n';
});
log(str);
log(`${header}\n${str}`);
}
+3 -3
View File
@@ -1,4 +1,4 @@
import type { WebExtRunInstance } from 'web-ext';
import type { WebExtRunInstance } from 'web-ext-run';
import { ExtensionRunner } from './ExtensionRunner';
/**
@@ -15,7 +15,7 @@ export function createWebExtRunner(): ExtensionRunner {
}
// Use the plugin's logger instead of web-ext's built-in one.
const webExtLogger = await import('web-ext/util/logger');
const webExtLogger = await import('web-ext-run/util/logger');
webExtLogger.consoleStream.write = ({ level, msg, name }) => {
if (level >= ERROR_LOG_LEVEL) config.logger.error(name, msg);
if (level >= WARN_LOG_LEVEL) config.logger.warn(msg);
@@ -55,7 +55,7 @@ export function createWebExtRunner(): ExtensionRunner {
config.logger.debug('web-ext config:', finalConfig);
config.logger.debug('web-ext options:', options);
const webExt = await import('web-ext');
const webExt = await import('web-ext-run');
runner = await webExt.default.cmd.run(finalConfig, options);
},
+32 -26
View File
@@ -2,42 +2,47 @@ import * as vite from 'vite';
import { Manifest, Scripting } from 'webextension-polyfill';
import { UnimportOptions } from 'unimport';
import { EntrypointGroup } from '.';
import { LogLevel } from 'consola';
export interface InlineConfig {
/**
* Project root directory.
* Your project's root directory containing the `package.json` used to fill out the
* `manifest.json`.
*
* @default
* process.cwd()
* @default process.cwd()
*/
root?: string;
/**
* Directory containing all source code. Set to `"src"` to move all source code to a `src/`
* directory.
*
* @default
* "<rootDir>"
* @default config.root
*/
srcDir?: string;
/**
* Directory containing files that will be copied to the output directory as-is.
*
* @default
* "<rootDir>/publicDir"
* @default "${config.root}/public"
*/
publicDir?: string;
/**
* @default
* "<srcDir>/entrypoints"
* @default "${config.srcDir}/entrypoints"
*/
entrypointsDir?: string;
/**
* > Only available when using the JS API. Not available in `wxt.config.ts` files
*
* Path to `"wxt.config.ts"` file or false to disable config file discovery.
*
* @default
* "wxt.config.ts"
* @default "wxt.config.ts"
*/
configFile?: string | false;
/**
* Set to `true` to show debug logs. Overriden by the command line `--debug` option.
*
* @default false
*/
debug?: boolean;
/**
* ID of the extension for each store. Used for publishing.
*/
@@ -56,7 +61,7 @@ export interface InlineConfig {
*/
imports?: Partial<UnimportOptions>;
/**
* Explicitly set a browser to target. This will override the default browser for each command,
* Explicitly set a browser to build for. This will override the default browser for each command,
* and can be overridden by the command line `--browser` option.
*
* @default
@@ -98,12 +103,12 @@ 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"
* - `{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"
*
* @default "{{name}}-{{version}}-{{browser}}.zip"
* @default "{name}-{version}-{browser}.zip"
*/
artifactTemplate?: string;
/**
@@ -111,22 +116,23 @@ 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"
* - `{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"
*
* @default "{{name}}-{{version}}-sources.zip"
* @default "{name}-{version}-sources.zip"
*/
sourcesTemplate?: string;
/**
* Override the artifactTemplate's `{{name}}` template variable. Defaults to the package.json's
* Override the artifactTemplate's `{name}` template variable. Defaults to the `package.json`'s
* name, or if that doesn't exist, the current working directories name.
*/
name?: string;
/**
* Root directory to ZIP. The ZIP can be uploaded to the Firefox Addon Store as your source
* code. Defaults to the `config.root` directory.
* Root directory to ZIP when generating the sources ZIP.
*
* @default config.root
*/
sourcesRoot?: string;
/**
@@ -221,6 +227,7 @@ export interface Logger {
error(...args: any[]): void;
fatal(...args: any[]): void;
success(...args: any[]): void;
level: LogLevel;
}
export interface BaseEntrypoint {
@@ -373,7 +380,6 @@ export type UserManifest = Partial<
| 'background'
| 'browser_action'
| 'chrome_url_overrides'
| 'content_scripts'
| 'devtools_page'
| 'manifest_version'
| 'options_page'
+1
View File
@@ -20,6 +20,7 @@ export interface InternalConfig {
entrypointsDir: string;
outBaseDir: string;
outDir: string;
debug: boolean;
storeIds: {
chrome?: string;
firefox?: string;
+1 -1
View File
@@ -10,7 +10,7 @@ export function getUnimportOptions(
imports: [{ name: 'defineConfig', from: 'wxt' }],
presets: [{ package: 'wxt/client' }, { package: 'wxt/browser' }],
warn: config.logger.warn,
dirs: ['./components/*', './composables/*', './hooks/*', './utils/*'],
dirs: ['components', 'composables', 'hooks', 'utils'],
};
return mergeConfig(
+5 -2
View File
@@ -9,7 +9,7 @@ import {
} from '../types';
import path, { resolve } from 'node:path';
import * as vite from 'vite';
import { consola } from 'consola';
import { LogLevels, consola } from 'consola';
import * as plugins from '../vite-plugins';
import { createFsCache } from './createFsCache';
import { getGlobals } from './globals';
@@ -33,6 +33,8 @@ export async function getInternalConfig(
const outBaseDir = path.resolve(root, '.output');
const outDir = path.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
const logger = config.logger ?? consola;
const debug = !!config.debug;
if (debug) logger.level = LogLevels.debug;
const baseConfig: InternalConfigNoUserDirs = {
root,
@@ -43,6 +45,7 @@ export async function getInternalConfig(
manifestVersion,
mode,
command,
debug,
logger,
vite: config.vite ?? {},
imports: config.imports ?? {},
@@ -81,7 +84,7 @@ export async function getInternalConfig(
userConfig.entrypointsDir ?? 'entrypoints',
);
const publicDir = resolve(root, userConfig.publicDir ?? 'public');
const wxtDir = resolve(srcDir, '.wxt');
const wxtDir = resolve(root, '.wxt');
const typesDir = resolve(wxtDir, 'types');
// Merge manifest sources
+52
View File
@@ -0,0 +1,52 @@
export interface Message {
name: string;
message: string;
description?: string;
}
const predefinedMessages = {
'@@extension_id': {
message: '<browser.runtime.id>',
description:
"The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even unlocalized extensions can use this message.\nNote: You can't use this message in a manifest file.",
},
'@@ui_locale': {
message: '<browser.i18n.getUiLocale()>',
description: '',
},
'@@bidi_dir': {
message: '<ltr|rtl>',
description:
'The text direction for the current locale, either "ltr" for left-to-right languages such as English or "rtl" for right-to-left languages such as Japanese.',
},
'@@bidi_reversed_dir': {
message: '<rtl|ltr>',
description:
'If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it\'s "ltr".',
},
'@@bidi_start_edge': {
message: '<left|right>',
description:
'If the @@bidi_dir is "ltr", then this is "left"; otherwise, it\'s "right".',
},
'@@bidi_end_edge': {
message: '<right|left>',
description:
'If the @@bidi_dir is "ltr", then this is "right"; otherwise, it\'s "left".',
},
};
/**
* Get a list of all messages and their metadata from JSON file contents.
*
* @param messagesJson The contents of a `_locales/en/messages.json` file.
*/
export function parseI18nMessages(messagesJson: object): Message[] {
return Object.entries({
...predefinedMessages,
...messagesJson,
}).map<Message>(([name, details]) => ({
name,
...details,
}));
}
+41 -1
View File
@@ -17,6 +17,7 @@ import {
mapWxtOptionsToContentScript,
} from './content-scripts';
import { getPackageJson } from './package';
import { normalizePath } from './paths';
/**
* Writes the manifest to the output directory and the build output.
@@ -65,6 +66,7 @@ export async function generateMainfest(
? pkg?.version
: undefined,
short_name: pkg?.shortName,
icons: discoverIcons(buildOutput),
},
config.manifest,
);
@@ -300,7 +302,7 @@ function addEntrypoints(
return map;
}, new Map<string, ContentScriptEntrypoint[]>());
manifest.content_scripts = Array.from(hashToEntrypointsMap.entries()).map(
const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
([, scripts]) => ({
...mapWxtOptionsToContentScript(scripts[0].options),
// TOOD: Sorting css and js arrays here so we get consistent test results... but we
@@ -313,10 +315,48 @@ function addEntrypoints(
.sort(),
}),
);
if (newContentScripts.length >= 0) {
manifest.content_scripts ??= [];
manifest.content_scripts.push(...newContentScripts);
}
}
}
}
function discoverIcons(
buildOutput: Omit<BuildOutput, 'manifest'>,
): Manifest.WebExtensionManifest['icons'] {
const icons: [string, string][] = [];
// prettier-ignore
// #region snippet
const iconRegex = [
/^icon-([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon-16.png
/^icon-([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon-16x16.png
/^icon@([0-9]+)w\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon@16w.png
/^icon@([0-9]+)h\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon@16h.png
/^icon@([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon@16.png
/^icon[\/\\]([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon/16.png
/^icon[\/\\]([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon/16x16.png
];
// #endregion snippet
buildOutput.publicAssets.forEach((asset) => {
let size: string | undefined;
for (const regex of iconRegex) {
const match = asset.fileName.match(regex);
if (match?.[1] != null) {
size = match[1];
break;
}
}
if (size == null) return;
icons.push([size, normalizePath(asset.fileName)]);
});
return icons.length > 0 ? Object.fromEntries(icons) : undefined;
}
function addDevModeCsp(
manifest: Manifest.WebExtensionManifest,
config: InternalConfig,
+3 -1
View File
@@ -33,7 +33,9 @@ export function multipageMove(
(entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath),
);
if (entrypoint == null) {
config.logger.debug('No entrypoint found for', oldBundlePath);
config.logger.debug(
`No entrypoint found for ${oldBundlePath}, leaving in chunks directory`,
);
continue;
}
+17 -7
View File
@@ -1,10 +1,20 @@
import { InternalConfig } from '../types';
import paths from 'vite-tsconfig-paths';
import * as vite from 'vite';
export function tsconfigPaths(config: InternalConfig) {
const fn: typeof paths =
typeof paths === 'function' ? paths : (paths as any).default;
return fn({
root: config.root,
});
export function tsconfigPaths(config: InternalConfig): vite.Plugin {
return {
name: 'wxt:aliases',
async config() {
return {
resolve: {
alias: {
'@@': config.root,
'~~': config.root,
'@': config.srcDir,
'~': config.srcDir,
},
},
};
},
};
}
+4 -2
View File
@@ -66,10 +66,12 @@ export async function zipExtension(
zipFiles.push(sourcesZipPath);
}
config.logger.success(
await printFileList(
config.logger.success,
`Zipped extension in ${formatDuration(Date.now() - start)}`,
config.outBaseDir,
zipFiles,
);
await printFileList(config.logger.log, config.outBaseDir, zipFiles);
return zipFiles;
}
+2
View File
@@ -159,6 +159,7 @@ export const fakeRollupOutputChunk = fakeObjectCreator<Rollup.OutputChunk>(
importedAssets: new Set(),
importedCss: new Set(),
},
preliminaryFileName: faker.string.alphanumeric(),
}),
);
@@ -212,6 +213,7 @@ export const fakeInternalConfig = fakeObjectCreator<InternalConfig>(() => ({
runnerConfig: {
config: {},
},
debug: faker.datatype.boolean(),
srcDir: fakeDir(),
storeIds: {},
typesDir: fakeDir(),
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -1,7 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
import './style.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
-9
View File
@@ -3,15 +3,6 @@ import react from '@vitejs/plugin-react';
// See https://wxt.dev/config.html
export default defineConfig({
manifest: {
icons: {
'16': 'icon/16.png',
'32': 'icon/32.png',
'48': 'icon/48.png',
'96': 'icon/96.png',
'128': 'icon/128.png',
},
},
vite: {
plugins: [react()],
},
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -1,6 +1,6 @@
import { render } from 'solid-js/web';
import './index.css';
import './style.css';
import App from './App';
render(() => <App />, document.getElementById('root')!);
-9
View File
@@ -3,15 +3,6 @@ import Solid from 'vite-plugin-solid';
// See https://wxt.dev/config.html
export default defineConfig({
manifest: {
icons: {
'16': 'icon/16.png',
'32': 'icon/32.png',
'48': 'icon/48.png',
'96': 'icon/96.png',
'128': 'icon/128.png',
},
},
vite: {
build: {
target: 'esnext',
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -1
View File
@@ -1,5 +1,5 @@
{
"extends": "./src/.wxt/tsconfig.json",
"extends": "./.wxt/tsconfig.json",
"compilerOptions": {
"useDefineForClassFields": true
}
-9
View File
@@ -4,15 +4,6 @@ import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte';
// See https://wxt.dev/config.html
export default defineConfig({
srcDir: 'src',
manifest: {
icons: {
'16': 'icon/16.png',
'32': 'icon/32.png',
'48': 'icon/48.png',
'96': 'icon/96.png',
'128': 'icon/128.png',
},
},
vite: {
plugins: [
svelte({
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+1 -11
View File
@@ -1,14 +1,4 @@
import { defineConfig } from 'wxt';
// See https://wxt.dev/config.html
export default defineConfig({
manifest: {
icons: {
'16': 'icon/16.png',
'32': 'icon/32.png',
'48': 'icon/48.png',
'96': 'icon/96.png',
'128': 'icon/128.png',
},
},
});
export default defineConfig({});
+2 -2
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import HelloWorld from '../../components/HelloWorld.vue';
import HelloWorld from '@/components/HelloWorld.vue';
</script>
<template>
@@ -8,7 +8,7 @@ import HelloWorld from '../../components/HelloWorld.vue';
<img src="/wxt.svg" class="logo" alt="WXT logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="../../assets/vue.svg" class="logo vue" alt="Vue logo" />
<img src="@/assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="WXT + Vue" />
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
-9
View File
@@ -3,15 +3,6 @@ import vue from '@vitejs/plugin-vue';
// See https://wxt.dev/config.html
export default defineConfig({
manifest: {
icons: {
'16': 'icon/16.png',
'32': 'icon/32.png',
'48': 'icon/48.png',
'96': 'icon/96.png',
'128': 'icon/128.png',
},
},
vite: {
plugins: [vue()],
},
+1 -1
View File
@@ -1,4 +1,4 @@
{
"extends": "./tsconfig.base.json",
"exclude": ["dist", "demo", "e2e/project", "templates"]
"exclude": ["dist", "demo", "e2e/dist", "templates"]
}
+2 -2
View File
@@ -16,7 +16,7 @@ const testSeed = (): Plugin => ({
});
// Clear e2e test projects
await fs.rm('e2e/project', { recursive: true, force: true });
await fs.rm('e2e/dist', { recursive: true, force: true });
export default defineWorkspace([
{
@@ -33,7 +33,7 @@ export default defineWorkspace([
name: 'e2e',
dir: 'e2e',
singleThread: true,
testTimeout: 20e3,
testTimeout: 60e3,
},
plugins: [testSeed()],
},