Compare commits

..

95 Commits

Author SHA1 Message Date
GitHub Actions 5801f96f79 chore(release): v0.5.5 2023-09-19 23:00:53 +00:00
Aaron Klinker 92039b855e fix: Automatically replace vite's process.env.NODE_ENV output in lib mode with the mode 2023-09-19 17:56:10 -05:00
GitHub Actions f145e00cc9 chore(release): v0.5.4 2023-09-19 22:39:57 +00:00
Aaron Klinker 87e8df90f5 fix: Don't warn about deep entrypoint subdirectories not being recognized 2023-09-19 17:32:25 -05:00
Aaron Klinker 419fab8193 fix: Recognize background/index.ts as an entrypoint 2023-09-19 17:32:25 -05:00
GitHub Actions 71743b3bf3 chore(release): v0.5.3 2023-09-19 21:40:41 +00:00
Aaron 8e3169eb1e chore: Refactor how config is resolved (#118) 2023-09-19 16:36:00 -05:00
Aaron Klinker 4ec904ea3c fix: Allow function for vite config 2023-09-19 12:53:35 -05:00
GitHub Actions b284305605 chore(release): v0.5.2 2023-09-19 15:55:16 +00:00
Aaron 234bd39a11 fix: Import client utils when getting entrypoint config (#117) 2023-09-19 10:50:41 -05:00
Aaron b5acea106d Bump templates to ^0.5.0 (#116) 2023-09-15 13:57:10 -05:00
GitHub Actions 33ed04ff6b chore(release): v0.5.1 2023-09-15 18:56:09 +00:00
Aaron f51bbc0a68 feat: Include/exclude entrypoints based on target browser (#115) 2023-09-15 13:51:53 -05:00
Aaron 6ee5b22e6c feat: Allow disabling auto-imports (#114) 2023-09-15 12:25:22 -05:00
Aaron Klinker b4de93d2e6 fix: Allow any string for target browser 2023-09-15 12:24:52 -05:00
Aaron Klinker 2570debc5c Update changelog 2023-09-14 15:19:32 -05:00
GitHub Actions e6a2c15d3c chore(release): v0.5.0 2023-09-14 20:14:05 +00:00
Aaron Klinker dbd84c8815 chore: Add entrypoints debug log 2023-09-14 15:03:50 -05:00
Aaron Klinker fe966b6ca8 docs: Add vite customization docs 2023-09-07 19:17:31 -05:00
Aaron Klinker e2350fecc6 docs: Add migration docs 2023-09-07 18:35:37 -05:00
Aaron b33b6631a5 docs: Make README pretty 2023-09-07 18:01:20 -05:00
Aaron Klinker 5f15f9ccec fix!: Change default publicDir to <srcDir>/public
This reverts 19c0948
2023-09-07 17:32:30 -05:00
Aaron Klinker beafa6afc4 docs: Fix typos 2023-09-07 16:14:36 -05:00
Aaron Klinker 46a5036482 docs: Add link to examples repo 2023-09-05 18:59:21 -05:00
dependabot[bot] eaa15e0d57 chore(deps-dev): bump @types/node from 20.5.0 to 20.5.9 (#110)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-05 18:25:22 -05:00
dependabot[bot] 2c22948d2e chore(deps): bump unimport from 3.1.3 to 3.3.0 (#108)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-05 18:25:04 -05:00
dependabot[bot] 3e701e22c9 chore(deps-dev): bump tsx from 3.12.7 to 3.12.8 (#109)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-05 18:23:33 -05:00
dependabot[bot] 6ee1fa17a1 chore(deps-dev): bump prettier from 3.0.1 to 3.0.3 (#111)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-05 18:23:06 -05:00
dependabot[bot] b76bf2831b chore(deps): bump linkedom from 0.15.2 to 0.15.3 (#112)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-05 18:21:04 -05:00
Aaron Klinker ac7cbfce1f chore: Move repo to wxt-dev org 2023-09-05 18:01:59 -05:00
Aaron Klinker 06f027b889 Update cli screenshot 2023-09-05 17:47:37 -05:00
Aaron Klinker e996686082 Update contributors in changelog 2023-09-05 17:36:48 -05:00
Aaron 1bb3474a91 fix(types): Don't write to files if nothing changes (#107) 2023-09-05 17:24:38 -05:00
GitHub Actions 79426671cf chore(release): v0.4.1 2023-09-05 22:15:02 +00:00
Aaron 89b34403ee feat(cli): Add wxt clean command to delete generated files (#106) 2023-09-05 16:21:57 -05:00
Aaron Klinker 749bc38234 Add sync-releases helper script 2023-09-05 10:21:14 -05:00
Aaron Klinker e086374828 fix(init): Don't show cd . when initializing the current directory 2023-09-05 10:21:14 -05:00
Aaron 2c3721e5f4 Upgrade templates to 0.4 (#105) 2023-09-05 10:16:31 -05:00
Aaron Klinker c2946aa1eb Fix changelog contributors 2023-09-05 10:14:22 -05:00
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
131 changed files with 3314 additions and 2897 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
+5 -1
View File
@@ -1,8 +1,12 @@
.output
coverage
dist
e2e/project
.wxt
docs/.vitepress/cache
pnpm-lock.yaml
CHANGELOG.md
.gitattributes
.gitignore
.prettierignore
*.png
*.svg
-1
View File
@@ -1,3 +1,2 @@
singleQuote: true
trailingComma: all
endOfLine: lf
+479 -274
View File
@@ -1,296 +1,501 @@
# Changelog
## v0.3.1
## v0.5.5
[compare changes](https://github.com/aklinker1/wxt/compare/v0.3.0...v0.3.1)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.5.4...v0.5.5)
### 🚀 Enhancements
### 🩹 Fixes
- CSS entrypoints ([#61](https://github.com/aklinker1/wxt/pull/61))
- `init` command for bootstrapping new projects ([#65](https://github.com/aklinker1/wxt/pull/65))
- Automatically replace vite's `process.env.NODE_ENV` output in lib mode with the mode ([92039b8](https://github.com/wxt-dev/wxt/commit/92039b8))
### 📖 Documentation
- Add zip command to installation scripts ([94a1097](https://github.com/aklinker1/wxt/commit/94a1097))
- Add output paths to entrypoint docs ([3a336eb](https://github.com/aklinker1/wxt/commit/3a336eb))
- Update installation docs ([aea866c](https://github.com/aklinker1/wxt/commit/aea866c))
- Add publishing docs ([4184b05](https://github.com/aklinker1/wxt/commit/4184b05))
- Add a section for extensions using WXT ([709b61a](https://github.com/aklinker1/wxt/commit/709b61a))
- Add a comparison page to compare and contrast against Plasmo ([38d4f9c](https://github.com/aklinker1/wxt/commit/38d4f9c))
### 🏡 Chore
- Update template projects to v0.3 ([#56](https://github.com/aklinker1/wxt/pull/56))
- Branding and logo ([#60](https://github.com/aklinker1/wxt/pull/60))
- Simplify binary setup ([#62](https://github.com/aklinker1/wxt/pull/62))
- Add Solid template ([#63](https://github.com/aklinker1/wxt/pull/63))
- Increate E2E test timeout to fix flakey test ([dfe424f](https://github.com/aklinker1/wxt/commit/dfe424f))
### 🤖 CI
- Speed up demo validation ([3a9fd39](https://github.com/aklinker1/wxt/commit/3a9fd39))
- Fix flakey failure when validating templates ([25677ba](https://github.com/aklinker1/wxt/commit/25677ba))
### ❤️ Contributors
### ❤️ Contributors
- Aaron Klinker <aaronklinker1@gmail.com>
- Aaron
- BeanWei <bean0wei@dingtalk.com>
## v0.3.0
## v0.5.4
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.5...v0.3.0)
### 🚀 Enhancements
- ⚠️ Add type safety to `browser.runtime.getURL` ([58a84ec](https://github.com/aklinker1/wxt/commit/58a84ec))
- ⚠️ Change default `publicDir` to `<rootDir>/public` ([19c0948](https://github.com/aklinker1/wxt/commit/19c0948))
- Windows support ([#50](https://github.com/aklinker1/wxt/pull/50))
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.5.3...v0.5.4)
### 🩹 Fixes
- Add `WebWorker` lib to generated tsconfig ([2c70246](https://github.com/aklinker1/wxt/commit/2c70246))
- Recognize `background/index.ts` as an entrypoint ([419fab8](https://github.com/wxt-dev/wxt/commit/419fab8))
- Don't warn about deep entrypoint subdirectories not being recognized ([87e8df9](https://github.com/wxt-dev/wxt/commit/87e8df9))
### 📖 Documentation
### ❤️ Contributors
- Update entrypoint directory links ([0aebb67](https://github.com/aklinker1/wxt/commit/0aebb67))
- Aaron Klinker <aaronklinker1@gmail.com>
### 🌊 Types
## v0.5.3
- Allow any string for the `__BROWSER__` global ([6092235](https://github.com/aklinker1/wxt/commit/6092235))
### 🤖 CI
- Improve checks against `demo/` extension ([9cc464f](https://github.com/aklinker1/wxt/commit/9cc464f))
#### ⚠️ Breaking Changes
- ⚠️ Add type safety to `browser.runtime.getURL` ([58a84ec](https://github.com/aklinker1/wxt/commit/58a84ec))
- ⚠️ Change default `publicDir` to `<rootDir>/public` ([19c0948](https://github.com/aklinker1/wxt/commit/19c0948))
### ❤️ Contributors
- Aaron Klinker
## v0.2.5
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.4...v0.2.5)
### 🚀 Enhancements
- Auto-import from subdirectories ([547fee0](https://github.com/aklinker1/wxt/commit/547fee0))
- Include background script in dev mode if user doesn't define one ([ca20a21](https://github.com/aklinker1/wxt/commit/ca20a21))
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.5.2...v0.5.3)
### 🩹 Fixes
- Don't crash when generating types in dev mode ([d8c1903](https://github.com/aklinker1/wxt/commit/d8c1903))
- Properly load entrypoints that reference `import.meta` ([54b18cc](https://github.com/aklinker1/wxt/commit/54b18cc))
- Allow function for vite config ([4ec904e](https://github.com/wxt-dev/wxt/commit/4ec904e))
### 🏡 Chore
- Update templates to wxt@0.2 ([9d00eb2](https://github.com/aklinker1/wxt/commit/9d00eb2))
### 🤖 CI
- Validate project templates ([9ac756f](https://github.com/aklinker1/wxt/commit/9ac756f))
- Refactor how config is resolved ([#118](https://github.com/wxt-dev/wxt/pull/118))
### ❤️ Contributors
- Aaron Klinker
- Aaron <aaronklinker1@gmail.com>
- Aaron Klinker <aaronklinker1@gmail.com>
## v0.2.4
## v0.5.2
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.3...v0.2.4)
### 🚀 Enhancements
- Add `wxt zip` command ([#47](https://github.com/aklinker1/wxt/pull/47))
### ❤️ Contributors
- Aaron
## v0.2.3
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.2...v0.2.3)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.5.1...v0.5.2)
### 🩹 Fixes
- Correctly lookup open port ([#45](https://github.com/aklinker1/wxt/pull/45))
- Read boolean maniest options from meta tags correctly ([495c5c8](https://github.com/aklinker1/wxt/commit/495c5c8))
- Some fields cannot be overridden from `config.manifest` ([#46](https://github.com/aklinker1/wxt/pull/46))
- Import client utils when getting entrypoint config ([#117](https://github.com/wxt-dev/wxt/pull/117))
### ❤️ Contributors
- Aaron Klinker
- Aaron <aaronklinker1@gmail.com>
## v0.2.2
## v0.5.1
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.1...v0.2.2)
### 🩹 Fixes
- Register content scripts correctly in dev mode ([2fb5a54](https://github.com/aklinker1/wxt/commit/2fb5a54))
### ❤️ Contributors
- Aaron Klinker
## v0.2.1
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.0...v0.2.1)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.5.0...v0.5.1)
### 🚀 Enhancements
- Support all content script options ([6f5bf89](https://github.com/aklinker1/wxt/commit/6f5bf89))
- Allow disabling auto-imports ([#114](https://github.com/wxt-dev/wxt/pull/114))
- Include/exclude entrypoints based on target browser ([#115](https://github.com/wxt-dev/wxt/pull/115))
### 🩹 Fixes
- Remove HMR log ([90fa6bf](https://github.com/aklinker1/wxt/commit/90fa6bf))
- Allow any string for target browser ([b4de93d](https://github.com/wxt-dev/wxt/commit/b4de93d))
### ❤️ Contributors
- Aaron Klinker
- Aaron <aaronklinker1@gmail.com>
- Aaron Klinker <aaronklinker1@gmail.com>
## v0.2.0
## v0.5.0
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.6...v0.2.0)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.4.1...v0.5.0)
### 🚀 Enhancements
### 🩹 Fixes
- ⚠️ Rename `defineBackgroundScript` to `defineBackground` ([5b48ae9](https://github.com/aklinker1/wxt/commit/5b48ae9))
- Recongize unnamed content scripts (`content.ts` and `content/index.ts`) ([3db5cec](https://github.com/aklinker1/wxt/commit/3db5cec))
- **types:** Don't write to files if nothing changes ([#107](https://github.com/wxt-dev/wxt/pull/107))
- ⚠️ Change default `publicDir` to `<srcDir>/public` ([5f15f9c](https://github.com/wxt-dev/wxt/commit/5f15f9c))
### 📖 Documentation
- Update templates ([f28a29e](https://github.com/aklinker1/wxt/commit/f28a29e))
- Add docs for each type of entrypoint ([77cbfc1](https://github.com/aklinker1/wxt/commit/77cbfc1))
- Add inline JSDoc for public types ([375a2a6](https://github.com/aklinker1/wxt/commit/375a2a6))
- Add link to examples repo ([46a5036](https://github.com/wxt-dev/wxt/commit/46a5036))
- Fix typos ([beafa6a](https://github.com/wxt-dev/wxt/commit/beafa6a))
- Make README pretty ([b33b663](https://github.com/wxt-dev/wxt/commit/b33b663))
- Add migration docs ([e2350fe](https://github.com/wxt-dev/wxt/commit/e2350fe))
- Add vite customization docs ([fe966b6](https://github.com/wxt-dev/wxt/commit/fe966b6))
### 🏡 Chore
- Run `wxt prepare` on `postinstall` ([c1ea9ba](https://github.com/aklinker1/wxt/commit/c1ea9ba))
- Don't format lockfile ([5c7e041](https://github.com/aklinker1/wxt/commit/5c7e041))
- Move repo to wxt-dev org ([ac7cbfc](https://github.com/wxt-dev/wxt/commit/ac7cbfc))
- **deps-dev:** Bump prettier from 3.0.1 to 3.0.3 ([#111](https://github.com/wxt-dev/wxt/pull/111))
- **deps-dev:** Bump tsx from 3.12.7 to 3.12.8 ([#109](https://github.com/wxt-dev/wxt/pull/109))
- **deps-dev:** Bump @types/node from 20.5.0 to 20.5.9 ([#110](https://github.com/wxt-dev/wxt/pull/110))
- Add entrypoints debug log ([dbd84c8](https://github.com/wxt-dev/wxt/commit/dbd84c8))
#### ⚠️ Breaking Changes
- ⚠️ Rename `defineBackgroundScript` to `defineBackground` ([5b48ae9](https://github.com/aklinker1/wxt/commit/5b48ae9))
- ⚠️ Change default `publicDir` to `<srcDir>/public` ([5f15f9c](https://github.com/wxt-dev/wxt/commit/5f15f9c))
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.6
## v0.4.1
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.5...v0.1.6)
### 🩹 Fixes
- Resolve tsconfig paths in vite ([ea92a27](https://github.com/aklinker1/wxt/commit/ea92a27))
- Add logs when a hot reload happens ([977246f](https://github.com/aklinker1/wxt/commit/977246f))
### 🏡 Chore
- React and Vue starter templates ([#33](https://github.com/aklinker1/wxt/pull/33))
- Svelte template ([#34](https://github.com/aklinker1/wxt/pull/34))
### ❤️ Contributors
- Aaron Klinker
- Aaron
## v0.1.5
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.4...v0.1.5)
### 🩹 Fixes
- Include `vite/client` types ([371be99](https://github.com/aklinker1/wxt/commit/371be99))
### ❤️ Contributors
- Aaron Klinker
## v0.1.4
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.3...v0.1.4)
### 🩹 Fixes
- Fix regression where manifest was not listed first in build summary ([fa2b656](https://github.com/aklinker1/wxt/commit/fa2b656))
- Fix config hook implementations for vite plugins ([49965e7](https://github.com/aklinker1/wxt/commit/49965e7))
### 📖 Documentation
- Update CLI screenshot ([0a26673](https://github.com/aklinker1/wxt/commit/0a26673))
### 🏡 Chore
- Update prettier ignore ([68611ae](https://github.com/aklinker1/wxt/commit/68611ae))
### ❤️ Contributors
- Aaron Klinker
## v0.1.3
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.2...v0.1.3)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.4.0...v0.4.1)
### 🚀 Enhancements
- Add tsconfig path aliases ([#32](https://github.com/aklinker1/wxt/pull/32))
- **cli:** Add `wxt clean` command to delete generated files ([#106](https://github.com/wxt-dev/wxt/pull/106))
### 🩹 Fixes
- Merge `manifest` option from both inline and user config ([05ca998](https://github.com/aklinker1/wxt/commit/05ca998))
- Cleanup build summary with sourcemaps ([ac0b28e](https://github.com/aklinker1/wxt/commit/ac0b28e))
### 📖 Documentation
- Create documentation site ([#31](https://github.com/aklinker1/wxt/pull/31))
### 🏡 Chore
- Upgrade to pnpm 8 ([0ce7c9d](https://github.com/aklinker1/wxt/commit/0ce7c9d))
- **init:** Don't show `cd .` when initializing the current directory ([e086374](https://github.com/wxt-dev/wxt/commit/e086374))
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.2
## v0.4.0
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.1...v0.1.2)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.3.2...v0.4.0)
### 🚀 Enhancements
- Accept a function for `config.manifest` ([ee49837](https://github.com/aklinker1/wxt/commit/ee49837))
- Add `--debug` flag for printing debug logs for all CLI commands ([#75](https://github.com/wxt-dev/wxt/pull/75))
- Replace `web-ext` with `web-ext-run` ([#101](https://github.com/wxt-dev/wxt/pull/101))
- Generate types for `browser.i18n.getMessage` ([#103](https://github.com/wxt-dev/wxt/pull/103))
### 🩹 Fixes
- Add missing types for `webextension-polyfill` and the `manifest` option ([636aa48](https://github.com/aklinker1/wxt/commit/636aa48))
- Only add imports to JS files ([b29c3c6](https://github.com/aklinker1/wxt/commit/b29c3c6))
- Generate valid type for `EntrypointPath` when there are no entrypoints ([6e7184d](https://github.com/aklinker1/wxt/commit/6e7184d))
- Allow adding custom content scripts ([b428a62](https://github.com/wxt-dev/wxt/commit/b428a62))
- Don't overwrite `wxt.config.ts` content scripts, append entrypoints to it ([5f5f1d9](https://github.com/wxt-dev/wxt/commit/5f5f1d9))
- ⚠️ Use relative path aliases inside `.wxt/tsconfig.json` ([#102](https://github.com/wxt-dev/wxt/pull/102))
### 📖 Documentation
- Add contribution guide ([#76](https://github.com/wxt-dev/wxt/pull/76))
### 🏡 Chore
- Setup dependabot for upgrading dependencies ([d66293c](https://github.com/wxt-dev/wxt/commit/d66293c))
- Update social preview ([e164bd5](https://github.com/wxt-dev/wxt/commit/e164bd5))
- Setup bug and feature issue templates ([2bde917](https://github.com/wxt-dev/wxt/commit/2bde917))
- Upgrade to prettier 3 ([#77](https://github.com/wxt-dev/wxt/pull/77))
- **deps-dev:** Bump vitest from 0.32.4 to 0.34.1 ([#81](https://github.com/wxt-dev/wxt/pull/81))
- **deps-dev:** Bump ora from 6.3.1 to 7.0.1 ([#79](https://github.com/wxt-dev/wxt/pull/79))
- **deps-dev:** Bump @types/node from 20.4.5 to 20.5.0 ([#78](https://github.com/wxt-dev/wxt/pull/78))
- **deps-dev:** Bump tsup from 7.1.0 to 7.2.0 ([#80](https://github.com/wxt-dev/wxt/pull/80))
- **deps-dev:** Bump @vitest/coverage-v8 from 0.32.4 to 0.34.1 ([#84](https://github.com/wxt-dev/wxt/pull/84))
- **deps-dev:** Bump vitepress from 1.0.0-beta.5 to 1.0.0-rc.4 ([#85](https://github.com/wxt-dev/wxt/pull/85))
- **deps-dev:** Bump vitest-mock-extended from 1.1.4 to 1.2.0 ([#87](https://github.com/wxt-dev/wxt/pull/87))
- **deps-dev:** Bump lint-staged from 13.3.0 to 14.0.0 ([#89](https://github.com/wxt-dev/wxt/pull/89))
- Fix remote code E2E test ([83e62a1](https://github.com/wxt-dev/wxt/commit/83e62a1))
- Fix failing demo build ([b58a15e](https://github.com/wxt-dev/wxt/commit/b58a15e))
- **deps-dev:** Bump vitest-mock-extended from 1.2.0 to 1.2.1 ([#97](https://github.com/wxt-dev/wxt/pull/97))
- **deps-dev:** Bump lint-staged from 14.0.0 to 14.0.1 ([#100](https://github.com/wxt-dev/wxt/pull/100))
- **deps-dev:** Bump vitest from 0.34.1 to 0.34.3 ([#99](https://github.com/wxt-dev/wxt/pull/99))
- Increase E2E test timeout because GitHub Actions Window runner is slow ([2a0842b](https://github.com/wxt-dev/wxt/commit/2a0842b))
- **deps-dev:** Bump vitepress from 1.0.0-rc.4 to 1.0.0-rc.10 ([#96](https://github.com/wxt-dev/wxt/pull/96))
- Fix test watcher restarting indefinetly ([2c7922c](https://github.com/wxt-dev/wxt/commit/2c7922c))
- Remove explict icon config from templates ([93bfee0](https://github.com/wxt-dev/wxt/commit/93bfee0))
- Use import aliases in Vue template ([#104](https://github.com/wxt-dev/wxt/pull/104))
#### ⚠️ Breaking Changes
- ⚠️ Use relative path aliases inside `.wxt/tsconfig.json` ([#102](https://github.com/wxt-dev/wxt/pull/102))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.3.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.3.1...v0.3.2)
### 🚀 Enhancements
- Discover icons from the public directory ([#72](https://github.com/wxt-dev/wxt/pull/72))
- Don't allow auto-importing from subdirectories ([d54d611](https://github.com/wxt-dev/wxt/commit/d54d611))
### 📖 Documentation
- Document the `url:` import prefix for remote code ([323045a](https://github.com/wxt-dev/wxt/commit/323045a))
- Fix typos ([97f0938](https://github.com/wxt-dev/wxt/commit/97f0938))
- Fix capitalization ([39467d1](https://github.com/wxt-dev/wxt/commit/39467d1))
- Generate markdown for config reference ([#74](https://github.com/wxt-dev/wxt/pull/74))
### 🏡 Chore
- Upgrade dependencies ([798f02f](https://github.com/wxt-dev/wxt/commit/798f02f))
- Upgrade vite (`v4.3` &rarr; `v4.4`) ([547c185](https://github.com/wxt-dev/wxt/commit/547c185))
- Update templates to work with CSS entrypoints ([7f15305](https://github.com/wxt-dev/wxt/commit/7f15305))
- Improve file list output in CI ([#73](https://github.com/wxt-dev/wxt/pull/73))
### 🤖 CI
- Validate templates against `main` ([#66](https://github.com/wxt-dev/wxt/pull/66))
- List vite version when validating project templates ([ef140dc](https://github.com/wxt-dev/wxt/commit/ef140dc))
- Validate templates using tarball to avoid version conflicts within the `wxt/node_modules` directory ([edfa075](https://github.com/wxt-dev/wxt/commit/edfa075))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.3.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.3.0...v0.3.1)
### 🚀 Enhancements
- CSS entrypoints ([#61](https://github.com/wxt-dev/wxt/pull/61))
- `init` command for bootstrapping new projects ([#65](https://github.com/wxt-dev/wxt/pull/65))
### 📖 Documentation
- Add zip command to installation scripts ([94a1097](https://github.com/wxt-dev/wxt/commit/94a1097))
- Add output paths to entrypoint docs ([3a336eb](https://github.com/wxt-dev/wxt/commit/3a336eb))
- Update installation docs ([aea866c](https://github.com/wxt-dev/wxt/commit/aea866c))
- Add publishing docs ([4184b05](https://github.com/wxt-dev/wxt/commit/4184b05))
- Add a section for extensions using WXT ([709b61a](https://github.com/wxt-dev/wxt/commit/709b61a))
- Add a comparison page to compare and contrast against Plasmo ([38d4f9c](https://github.com/wxt-dev/wxt/commit/38d4f9c))
### 🏡 Chore
- Update template projects to v0.3 ([#56](https://github.com/wxt-dev/wxt/pull/56))
- Branding and logo ([#60](https://github.com/wxt-dev/wxt/pull/60))
- Simplify binary setup ([#62](https://github.com/wxt-dev/wxt/pull/62))
- Add Solid template ([#63](https://github.com/wxt-dev/wxt/pull/63))
- Increate E2E test timeout to fix flakey test ([dfe424f](https://github.com/wxt-dev/wxt/commit/dfe424f))
### 🤖 CI
- Speed up demo validation ([3a9fd39](https://github.com/wxt-dev/wxt/commit/3a9fd39))
- Fix flakey failure when validating templates ([25677ba](https://github.com/wxt-dev/wxt/commit/25677ba))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
- BeanWei ([@BeanWei](https://github.com/BeanWei))
## v0.3.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.2.5...v0.3.0)
### 🚀 Enhancements
- ⚠️ Add type safety to `browser.runtime.getURL` ([58a84ec](https://github.com/wxt-dev/wxt/commit/58a84ec))
- ⚠️ Change default `publicDir` to `<rootDir>/public` ([19c0948](https://github.com/wxt-dev/wxt/commit/19c0948))
- Windows support ([#50](https://github.com/wxt-dev/wxt/pull/50))
### 🩹 Fixes
- Add `WebWorker` lib to generated tsconfig ([2c70246](https://github.com/wxt-dev/wxt/commit/2c70246))
### 📖 Documentation
- Update entrypoint directory links ([0aebb67](https://github.com/wxt-dev/wxt/commit/0aebb67))
### 🌊 Types
- Change `config.vite` to `UserConfig` ([ef6001e](https://github.com/aklinker1/wxt/commit/ef6001e))
- Allow any string for the `__BROWSER__` global ([6092235](https://github.com/wxt-dev/wxt/commit/6092235))
### ❤️ Contributors
### 🤖 CI
- Aaron Klinker
- Improve checks against `demo/` extension ([9cc464f](https://github.com/wxt-dev/wxt/commit/9cc464f))
## v0.1.1
#### ⚠️ Breaking Changes
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.0...v0.1.1)
- ⚠️ Add type safety to `browser.runtime.getURL` ([58a84ec](https://github.com/wxt-dev/wxt/commit/58a84ec))
- ⚠️ Change default `publicDir` to `<rootDir>/public` ([19c0948](https://github.com/wxt-dev/wxt/commit/19c0948))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.5
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.2.4...v0.2.5)
### 🚀 Enhancements
- Auto-import from subdirectories ([547fee0](https://github.com/wxt-dev/wxt/commit/547fee0))
- Include background script in dev mode if user doesn't define one ([ca20a21](https://github.com/wxt-dev/wxt/commit/ca20a21))
### 🩹 Fixes
- Allow dashes in entrypoint names ([2e51e73](https://github.com/aklinker1/wxt/commit/2e51e73))
- Unable to read entrypoint options ([#28](https://github.com/aklinker1/wxt/pull/28))
- Don't crash when generating types in dev mode ([d8c1903](https://github.com/wxt-dev/wxt/commit/d8c1903))
- Properly load entrypoints that reference `import.meta` ([54b18cc](https://github.com/wxt-dev/wxt/commit/54b18cc))
### 🏡 Chore
- Update templates to wxt@0.2 ([9d00eb2](https://github.com/wxt-dev/wxt/commit/9d00eb2))
### 🤖 CI
- Validate project templates ([9ac756f](https://github.com/wxt-dev/wxt/commit/9ac756f))
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.4
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.2.3...v0.2.4)
### 🚀 Enhancements
- Add `wxt zip` command ([#47](https://github.com/wxt-dev/wxt/pull/47))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.2.2...v0.2.3)
### 🩹 Fixes
- Correctly lookup open port ([#45](https://github.com/wxt-dev/wxt/pull/45))
- Read boolean maniest options from meta tags correctly ([495c5c8](https://github.com/wxt-dev/wxt/commit/495c5c8))
- Some fields cannot be overridden from `config.manifest` ([#46](https://github.com/wxt-dev/wxt/pull/46))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.2.1...v0.2.2)
### 🩹 Fixes
- Register content scripts correctly in dev mode ([2fb5a54](https://github.com/wxt-dev/wxt/commit/2fb5a54))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.2.0...v0.2.1)
### 🚀 Enhancements
- Support all content script options ([6f5bf89](https://github.com/wxt-dev/wxt/commit/6f5bf89))
### 🩹 Fixes
- Remove HMR log ([90fa6bf](https://github.com/wxt-dev/wxt/commit/90fa6bf))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.2.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.1.6...v0.2.0)
### 🚀 Enhancements
- ⚠️ Rename `defineBackgroundScript` to `defineBackground` ([5b48ae9](https://github.com/wxt-dev/wxt/commit/5b48ae9))
- Recongize unnamed content scripts (`content.ts` and `content/index.ts`) ([3db5cec](https://github.com/wxt-dev/wxt/commit/3db5cec))
### 📖 Documentation
- Update templates ([f28a29e](https://github.com/wxt-dev/wxt/commit/f28a29e))
- Add docs for each type of entrypoint ([77cbfc1](https://github.com/wxt-dev/wxt/commit/77cbfc1))
- Add inline JSDoc for public types ([375a2a6](https://github.com/wxt-dev/wxt/commit/375a2a6))
### 🏡 Chore
- Run `wxt prepare` on `postinstall` ([c1ea9ba](https://github.com/wxt-dev/wxt/commit/c1ea9ba))
- Don't format lockfile ([5c7e041](https://github.com/wxt-dev/wxt/commit/5c7e041))
#### ⚠️ Breaking Changes
- ⚠️ Rename `defineBackgroundScript` to `defineBackground` ([5b48ae9](https://github.com/wxt-dev/wxt/commit/5b48ae9))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.6
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.1.5...v0.1.6)
### 🩹 Fixes
- Resolve tsconfig paths in vite ([ea92a27](https://github.com/wxt-dev/wxt/commit/ea92a27))
- Add logs when a hot reload happens ([977246f](https://github.com/wxt-dev/wxt/commit/977246f))
### 🏡 Chore
- React and Vue starter templates ([#33](https://github.com/wxt-dev/wxt/pull/33))
- Svelte template ([#34](https://github.com/wxt-dev/wxt/pull/34))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.5
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.1.4...v0.1.5)
### 🩹 Fixes
- Include `vite/client` types ([371be99](https://github.com/wxt-dev/wxt/commit/371be99))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.4
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.1.3...v0.1.4)
### 🩹 Fixes
- Fix regression where manifest was not listed first in build summary ([fa2b656](https://github.com/wxt-dev/wxt/commit/fa2b656))
- Fix config hook implementations for vite plugins ([49965e7](https://github.com/wxt-dev/wxt/commit/49965e7))
### 📖 Documentation
- Update CLI screenshot ([0a26673](https://github.com/wxt-dev/wxt/commit/0a26673))
### 🏡 Chore
- Update prettier ignore ([68611ae](https://github.com/wxt-dev/wxt/commit/68611ae))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.3
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.1.2...v0.1.3)
### 🚀 Enhancements
- Add tsconfig path aliases ([#32](https://github.com/wxt-dev/wxt/pull/32))
### 🩹 Fixes
- Merge `manifest` option from both inline and user config ([05ca998](https://github.com/wxt-dev/wxt/commit/05ca998))
- Cleanup build summary with sourcemaps ([ac0b28e](https://github.com/wxt-dev/wxt/commit/ac0b28e))
### 📖 Documentation
- Create documentation site ([#31](https://github.com/wxt-dev/wxt/pull/31))
### 🏡 Chore
- Upgrade to pnpm 8 ([0ce7c9d](https://github.com/wxt-dev/wxt/commit/0ce7c9d))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.2
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.1.1...v0.1.2)
### 🚀 Enhancements
- Accept a function for `config.manifest` ([ee49837](https://github.com/wxt-dev/wxt/commit/ee49837))
### 🩹 Fixes
- Add missing types for `webextension-polyfill` and the `manifest` option ([636aa48](https://github.com/wxt-dev/wxt/commit/636aa48))
- Only add imports to JS files ([b29c3c6](https://github.com/wxt-dev/wxt/commit/b29c3c6))
- Generate valid type for `EntrypointPath` when there are no entrypoints ([6e7184d](https://github.com/wxt-dev/wxt/commit/6e7184d))
### 🌊 Types
- Change `config.vite` to `UserConfig` ([ef6001e](https://github.com/wxt-dev/wxt/commit/ef6001e))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.1
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.1.0...v0.1.1)
### 🩹 Fixes
- Allow dashes in entrypoint names ([2e51e73](https://github.com/wxt-dev/wxt/commit/2e51e73))
- Unable to read entrypoint options ([#28](https://github.com/wxt-dev/wxt/pull/28))
### ❤️ Contributors
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.1.0
@@ -301,128 +506,128 @@ Initial release of WXT. Full support for production builds and initial toolkit f
- Reload HTML pages when saving them directly
- Re-register and reload tabs when content scripts change
[compare changes](https://github.com/aklinker1/wxt/compare/v0.0.2...v0.1.0)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.0.2...v0.1.0)
### 🚀 Enhancements
- Content scripts reloading ([#25](https://github.com/aklinker1/wxt/pull/25))
- Content scripts reloading ([#25](https://github.com/wxt-dev/wxt/pull/25))
### 📖 Documentation
- Update feature list ([0255028](https://github.com/aklinker1/wxt/commit/0255028))
- Update feature list ([0255028](https://github.com/wxt-dev/wxt/commit/0255028))
### 🤖 CI
- Create github release ([b7c078f](https://github.com/aklinker1/wxt/commit/b7c078f))
- Create github release ([b7c078f](https://github.com/wxt-dev/wxt/commit/b7c078f))
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.0.2
[compare changes](https://github.com/aklinker1/wxt/compare/v0.0.1...v0.0.2)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.0.1...v0.0.2)
### 🚀 Enhancements
- Reload extension when source code is changed ([#17](https://github.com/aklinker1/wxt/pull/17))
- Setup background script web socket/reload ([#22](https://github.com/aklinker1/wxt/pull/22))
- Reload HTML files individually ([#23](https://github.com/aklinker1/wxt/pull/23))
- Reload extension when source code is changed ([#17](https://github.com/wxt-dev/wxt/pull/17))
- Setup background script web socket/reload ([#22](https://github.com/wxt-dev/wxt/pull/22))
- Reload HTML files individually ([#23](https://github.com/wxt-dev/wxt/pull/23))
### 🩹 Fixes
- Output chunks to a chunks directory ([2dd7a99](https://github.com/aklinker1/wxt/commit/2dd7a99))
- Remove hash from content script css outputs ([#20](https://github.com/aklinker1/wxt/pull/20))
- Overwrite files with the same name when renaming entrypoints in dev mode ([37986bf](https://github.com/aklinker1/wxt/commit/37986bf))
- Separate template builds to prevent sharing chunks ([7f3a1e8](https://github.com/aklinker1/wxt/commit/7f3a1e8))
- Show Vite warnings and errors ([c51f0e0](https://github.com/aklinker1/wxt/commit/c51f0e0))
- Output chunks to a chunks directory ([2dd7a99](https://github.com/wxt-dev/wxt/commit/2dd7a99))
- Remove hash from content script css outputs ([#20](https://github.com/wxt-dev/wxt/pull/20))
- Overwrite files with the same name when renaming entrypoints in dev mode ([37986bf](https://github.com/wxt-dev/wxt/commit/37986bf))
- Separate template builds to prevent sharing chunks ([7f3a1e8](https://github.com/wxt-dev/wxt/commit/7f3a1e8))
- Show Vite warnings and errors ([c51f0e0](https://github.com/wxt-dev/wxt/commit/c51f0e0))
### 📖 Documentation
- Add milestone progress badge to README ([684197d](https://github.com/aklinker1/wxt/commit/684197d))
- Fix milestone link in README ([e14f81d](https://github.com/aklinker1/wxt/commit/e14f81d))
- Add milestone progress badge to README ([684197d](https://github.com/wxt-dev/wxt/commit/684197d))
- Fix milestone link in README ([e14f81d](https://github.com/wxt-dev/wxt/commit/e14f81d))
### 🏡 Chore
- Refactor build output type ([#19](https://github.com/aklinker1/wxt/pull/19))
- Refactor build outputs to support transpiled templates ([a78aada](https://github.com/aklinker1/wxt/commit/a78aada))
- Rename `templates` to `virtual-modules` ([#24](https://github.com/aklinker1/wxt/pull/24))
- Update cli screenshot ([54eb118](https://github.com/aklinker1/wxt/commit/54eb118))
- Refactor build output type ([#19](https://github.com/wxt-dev/wxt/pull/19))
- Refactor build outputs to support transpiled templates ([a78aada](https://github.com/wxt-dev/wxt/commit/a78aada))
- Rename `templates` to `virtual-modules` ([#24](https://github.com/wxt-dev/wxt/pull/24))
- Update cli screenshot ([54eb118](https://github.com/wxt-dev/wxt/commit/54eb118))
### ❤️ Contributors
- Aaron Klinker
- Aaron Klinker ([@aklinker1](https://github.com/aklinker1))
## v0.0.1
[compare changes](https://github.com/aklinker1/wxt/compare/v0.0.0...v0.0.1)
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.0.0...v0.0.1)
### 🚀 Enhancements
- Add logger to config ([232ff7a](https://github.com/aklinker1/wxt/commit/232ff7a))
- Export and bootstrap the `/client` package ([5b07c95](https://github.com/aklinker1/wxt/commit/5b07c95))
- Resolve entrypoints based on filesystem ([a63f061](https://github.com/aklinker1/wxt/commit/a63f061))
- Separate output directories for each browser/manifest version ([f09ffbb](https://github.com/aklinker1/wxt/commit/f09ffbb))
- Build entrypoints and output `manfiest.json` ([1e7c738](https://github.com/aklinker1/wxt/commit/1e7c738))
- Automatically add CSS files to content scripts ([047ce04](https://github.com/aklinker1/wxt/commit/047ce04))
- Download and bundle remote URL imports ([523c7df](https://github.com/aklinker1/wxt/commit/523c7df))
- Generate type declarations and config for project types and auto-imports ([21debad](https://github.com/aklinker1/wxt/commit/21debad))
- Good looking console output ([e2cc995](https://github.com/aklinker1/wxt/commit/e2cc995))
- Dev server working and a valid extension is built ([505e419](https://github.com/aklinker1/wxt/commit/505e419))
- Virtualized content script entrypoint ([ca29537](https://github.com/aklinker1/wxt/commit/ca29537))
- Provide custom, typed globals defined by Vite ([8c59a1c](https://github.com/aklinker1/wxt/commit/8c59a1c))
- Copy public directory to outputs ([1a25f2b](https://github.com/aklinker1/wxt/commit/1a25f2b))
- Support browser and chrome styles for mv2 popups ([7945c94](https://github.com/aklinker1/wxt/commit/7945c94))
- Support browser and chrome styles for mv2 popups ([7abb577](https://github.com/aklinker1/wxt/commit/7abb577))
- Support more CLI flags for `build` and `dev` ([#9](https://github.com/aklinker1/wxt/pull/9))
- Add more supported browser types ([f114c5b](https://github.com/aklinker1/wxt/commit/f114c5b))
- Open browser when starting dev server ([#11](https://github.com/aklinker1/wxt/pull/11))
- Add logger to config ([232ff7a](https://github.com/wxt-dev/wxt/commit/232ff7a))
- Export and bootstrap the `/client` package ([5b07c95](https://github.com/wxt-dev/wxt/commit/5b07c95))
- Resolve entrypoints based on filesystem ([a63f061](https://github.com/wxt-dev/wxt/commit/a63f061))
- Separate output directories for each browser/manifest version ([f09ffbb](https://github.com/wxt-dev/wxt/commit/f09ffbb))
- Build entrypoints and output `manfiest.json` ([1e7c738](https://github.com/wxt-dev/wxt/commit/1e7c738))
- Automatically add CSS files to content scripts ([047ce04](https://github.com/wxt-dev/wxt/commit/047ce04))
- Download and bundle remote URL imports ([523c7df](https://github.com/wxt-dev/wxt/commit/523c7df))
- Generate type declarations and config for project types and auto-imports ([21debad](https://github.com/wxt-dev/wxt/commit/21debad))
- Good looking console output ([e2cc995](https://github.com/wxt-dev/wxt/commit/e2cc995))
- Dev server working and a valid extension is built ([505e419](https://github.com/wxt-dev/wxt/commit/505e419))
- Virtualized content script entrypoint ([ca29537](https://github.com/wxt-dev/wxt/commit/ca29537))
- Provide custom, typed globals defined by Vite ([8c59a1c](https://github.com/wxt-dev/wxt/commit/8c59a1c))
- Copy public directory to outputs ([1a25f2b](https://github.com/wxt-dev/wxt/commit/1a25f2b))
- Support browser and chrome styles for mv2 popups ([7945c94](https://github.com/wxt-dev/wxt/commit/7945c94))
- Support browser and chrome styles for mv2 popups ([7abb577](https://github.com/wxt-dev/wxt/commit/7abb577))
- Support more CLI flags for `build` and `dev` ([#9](https://github.com/wxt-dev/wxt/pull/9))
- Add more supported browser types ([f114c5b](https://github.com/wxt-dev/wxt/commit/f114c5b))
- Open browser when starting dev server ([#11](https://github.com/wxt-dev/wxt/pull/11))
### 🩹 Fixes
- Support `srcDir` config ([739d19f](https://github.com/aklinker1/wxt/commit/739d19f))
- Root path customization now works ([4faa3b3](https://github.com/aklinker1/wxt/commit/4faa3b3))
- Print durations as ms/s based on total time ([3e37de9](https://github.com/aklinker1/wxt/commit/3e37de9))
- Don't print error twice when background crashes ([407627c](https://github.com/aklinker1/wxt/commit/407627c))
- Load package.json from root not cwd ([3ca16ee](https://github.com/aklinker1/wxt/commit/3ca16ee))
- Only allow a single entrypoint with a given name ([8eb4e86](https://github.com/aklinker1/wxt/commit/8eb4e86))
- Respect the mv2 popup type ([0f37ceb](https://github.com/aklinker1/wxt/commit/0f37ceb))
- Respect background type and persistent manifest options ([573ef80](https://github.com/aklinker1/wxt/commit/573ef80))
- Make content script array orders consistent ([f380378](https://github.com/aklinker1/wxt/commit/f380378))
- Firefox manifest warnings in dev mode ([50bb845](https://github.com/aklinker1/wxt/commit/50bb845))
- Support `srcDir` config ([739d19f](https://github.com/wxt-dev/wxt/commit/739d19f))
- Root path customization now works ([4faa3b3](https://github.com/wxt-dev/wxt/commit/4faa3b3))
- Print durations as ms/s based on total time ([3e37de9](https://github.com/wxt-dev/wxt/commit/3e37de9))
- Don't print error twice when background crashes ([407627c](https://github.com/wxt-dev/wxt/commit/407627c))
- Load package.json from root not cwd ([3ca16ee](https://github.com/wxt-dev/wxt/commit/3ca16ee))
- Only allow a single entrypoint with a given name ([8eb4e86](https://github.com/wxt-dev/wxt/commit/8eb4e86))
- Respect the mv2 popup type ([0f37ceb](https://github.com/wxt-dev/wxt/commit/0f37ceb))
- Respect background type and persistent manifest options ([573ef80](https://github.com/wxt-dev/wxt/commit/573ef80))
- Make content script array orders consistent ([f380378](https://github.com/wxt-dev/wxt/commit/f380378))
- Firefox manifest warnings in dev mode ([50bb845](https://github.com/wxt-dev/wxt/commit/50bb845))
### 📖 Documentation
- Update README ([785ea54](https://github.com/aklinker1/wxt/commit/785ea54))
- Update README ([99ccadb](https://github.com/aklinker1/wxt/commit/99ccadb))
- Update description ([07a262e](https://github.com/aklinker1/wxt/commit/07a262e))
- Update README ([58a0ef4](https://github.com/aklinker1/wxt/commit/58a0ef4))
- Update README ([23ed6f7](https://github.com/aklinker1/wxt/commit/23ed6f7))
- Add initial release milestone link to README ([b400e54](https://github.com/aklinker1/wxt/commit/b400e54))
- Fix typo in README ([5590c9d](https://github.com/aklinker1/wxt/commit/5590c9d))
- Update README ([785ea54](https://github.com/wxt-dev/wxt/commit/785ea54))
- Update README ([99ccadb](https://github.com/wxt-dev/wxt/commit/99ccadb))
- Update description ([07a262e](https://github.com/wxt-dev/wxt/commit/07a262e))
- Update README ([58a0ef4](https://github.com/wxt-dev/wxt/commit/58a0ef4))
- Update README ([23ed6f7](https://github.com/wxt-dev/wxt/commit/23ed6f7))
- Add initial release milestone link to README ([b400e54](https://github.com/wxt-dev/wxt/commit/b400e54))
- Fix typo in README ([5590c9d](https://github.com/wxt-dev/wxt/commit/5590c9d))
### 🏡 Chore
- Refactor cli files into their own directory ([e6c0d84](https://github.com/aklinker1/wxt/commit/e6c0d84))
- Simplify `BuildOutput` type ([1f6c4a0](https://github.com/aklinker1/wxt/commit/1f6c4a0))
- Move `.exvite` directory into `srcDir` instead of `root` ([53fb805](https://github.com/aklinker1/wxt/commit/53fb805))
- Refactor CLI commands ([b8952b6](https://github.com/aklinker1/wxt/commit/b8952b6))
- Improve build summary sorting ([ec57e8c](https://github.com/aklinker1/wxt/commit/ec57e8c))
- Remove comments ([e3e9c0d](https://github.com/aklinker1/wxt/commit/e3e9c0d))
- Refactor internal config creation ([7c634f4](https://github.com/aklinker1/wxt/commit/7c634f4))
- Check virtual entrypoints feature in README ([70208f4](https://github.com/aklinker1/wxt/commit/70208f4))
- Add E2E tests and convert to vitest workspace ([5813302](https://github.com/aklinker1/wxt/commit/5813302))
- Rename package to wxt ([51a1072](https://github.com/aklinker1/wxt/commit/51a1072))
- Fix header log's timestamp ([8ca5657](https://github.com/aklinker1/wxt/commit/8ca5657))
- Fix demo global usage ([1ecfedd](https://github.com/aklinker1/wxt/commit/1ecfedd))
- Refactor folder structure ([9ab3953](https://github.com/aklinker1/wxt/commit/9ab3953))
- Fix release workflow ([2e94f2a](https://github.com/aklinker1/wxt/commit/2e94f2a))
- Refactor cli files into their own directory ([e6c0d84](https://github.com/wxt-dev/wxt/commit/e6c0d84))
- Simplify `BuildOutput` type ([1f6c4a0](https://github.com/wxt-dev/wxt/commit/1f6c4a0))
- Move `.exvite` directory into `srcDir` instead of `root` ([53fb805](https://github.com/wxt-dev/wxt/commit/53fb805))
- Refactor CLI commands ([b8952b6](https://github.com/wxt-dev/wxt/commit/b8952b6))
- Improve build summary sorting ([ec57e8c](https://github.com/wxt-dev/wxt/commit/ec57e8c))
- Remove comments ([e3e9c0d](https://github.com/wxt-dev/wxt/commit/e3e9c0d))
- Refactor internal config creation ([7c634f4](https://github.com/wxt-dev/wxt/commit/7c634f4))
- Check virtual entrypoints feature in README ([70208f4](https://github.com/wxt-dev/wxt/commit/70208f4))
- Add E2E tests and convert to vitest workspace ([5813302](https://github.com/wxt-dev/wxt/commit/5813302))
- Rename package to wxt ([51a1072](https://github.com/wxt-dev/wxt/commit/51a1072))
- Fix header log's timestamp ([8ca5657](https://github.com/wxt-dev/wxt/commit/8ca5657))
- Fix demo global usage ([1ecfedd](https://github.com/wxt-dev/wxt/commit/1ecfedd))
- Refactor folder structure ([9ab3953](https://github.com/wxt-dev/wxt/commit/9ab3953))
- Fix release workflow ([2e94f2a](https://github.com/wxt-dev/wxt/commit/2e94f2a))
### 🤖 CI
- Create validation workflow ([#12](https://github.com/aklinker1/wxt/pull/12))
- Create release workflow ([#13](https://github.com/aklinker1/wxt/pull/13))
- Create validation workflow ([#12](https://github.com/wxt-dev/wxt/pull/12))
- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13))
### ❤️ 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.
+33 -10
View File
@@ -1,11 +1,38 @@
<h1 align="center">WXT</h1>
<h1 align="center">
<img style="vertical-align:middle" width="44" src="./docs/public/hero-logo.svg" alt="WXT Logo">
<span>WXT</span>
</h1>
<p align="center"><img align="center" width="44" src="./docs/public/hero-logo.svg" alt="WXT Logo"></p>
<p align="center">
<span>Next-gen framework for developing web extensions.</span>
<br/>
<span>⚡</span>
<br/>
<q><i>It's like Nuxt, but for Chrome Extensions</i></q>
</p>
<p align="center"><i>Next gen framework for developing web extensions.<br/>Powered by <a href="https://vitejs.dev/" target="_blank">Vite</a>. Inspired by <a href="https://nuxt.com/" target="_blank">Nuxt</a>.</i></p>
<p align="center">
<a href="https://wxt.dev" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/get-started/installation.html" target="_blank">Installation</a>
&bull;
<a href="https://wxt.dev/guide/background.html" target="_blank">Entrypoints</a>
&bull;
<a href="https://wxt.dev/config.html" target="_blank">Configuration</a>
</p>
![Example CLI Output](./docs/assets/cli-output.png)
## Quick Start
Bootstrap a new project:
```sh
pnpx wxt@latest init <project-name>
```
Or see the [installation guide](https://wxt.dev/get-started/installation.html) to get started with WXT.
## Features
- 🌐 Supports all browsers
@@ -18,17 +45,13 @@
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
- 🖍️ Quickly bootstrap a new project
### Todo
### Coming Soon
- 📏 Bundle analysis
- 🤖 Automated publishing
## Get Started
Checkout the [installation guide](https://wxt.dev/get-started/installation.html) to get started with WXT.
## Contributors
<a href="https://github.com/aklinker1/wxt/graphs/contributors">
<img src="https://contrib.rocks/image?repo=aklinker1/wxt" />
<a href="https://github.com/wxt-dev/wxt/graphs/contributors">
<img src="https://contrib.rocks/image?repo=wxt-dev/wxt" />
</a>
+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" />
+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"
}
}
}
}

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 504 B

After

Width:  |  Height:  |  Size: 504 B

Before

Width:  |  Height:  |  Size: 936 B

After

Width:  |  Height:  |  Size: 936 B

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

+1 -1
View File
@@ -1,3 +1,3 @@
{
"extends": ["../tsconfig.base.json", "./src/.wxt/tsconfig.json"]
"extends": ["../tsconfig.base.json", "./.wxt/tsconfig.json"]
}
+1 -10
View File
@@ -2,16 +2,7 @@ import { defineConfig } from 'wxt';
export default defineConfig({
srcDir: 'src',
storeIds: {
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',
},
});
+9 -2
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,
@@ -10,7 +15,7 @@ export default defineConfig({
// https://vitepress.dev/reference/default-theme-config
logo: '/logo.svg',
editLink: {
pattern: 'https://github.com/aklinker1/wxt/edit/main/docs/:path',
pattern: 'https://github.com/wxt-dev/wxt/edit/main/docs/:path',
},
search: {
provider: 'local',
@@ -47,6 +52,8 @@ 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' },
{ text: 'Vite', link: '/guide/vite.md' },
],
},
{
@@ -70,6 +77,6 @@ export default defineConfig({
],
},
socialLinks: [{ icon: 'github', link: 'https://github.com/aklinker1/wxt' }],
socialLinks: [{ icon: 'github', link: 'https://github.com/wxt-dev/wxt' }],
},
});
@@ -0,0 +1,145 @@
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()`.
*
* This also stops any further type inspection for objects, meaning the object will be documented,
* not it's properties.
*/
const CUSTOM_TYPES = {
manifest:
'Manifest | Promise<Manifest> | () => Manifest | () => Promise<Manifest>',
imports: "false | Partial<import('unimport').UnimportOptions>",
vite: "Omit<import('vite').UserConfig, 'root' | 'configFile' | 'mode'>",
};
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() && !CUSTOM_TYPES[pathStr]) {
return type.getProperties().flatMap((property) => {
const childPath = [...path, property.getName()];
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,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

After

Width:  |  Height:  |  Size: 238 KiB

-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 }}
+2 -2
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 | ❌ | ✅ |
@@ -45,5 +45,5 @@ Reloading each part of the extension individually improves your iteration speed
WXT solves this problem by reloading HTML pages and content scripts individually (when possible) to keep your UIs open while you develop them. This is a MV3 feature, so if you're developing a MV2 extension, you'll get the same dev experience as Plasmo.
:::info
Unfortunately, there isn't an API for reloading the background page/service worker individually, so if you change a file used by the background, the entire extension will reload. See [Issue #53](https://github.com/aklinker1/wxt/issues/53) for more details.
Unfortunately, there isn't an API for reloading the background page/service worker individually, so if you change a file used by the background, the entire extension will reload. See [Issue #53](https://github.com/wxt-dev/wxt/issues/53) for more details.
:::
+9 -10
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
@@ -12,12 +12,11 @@ For example, a project that looks like this:
```
<rootDir>
─ entrypoints/
├─ background.ts
├─ content.ts
├─ injected.ts
└─ popup.html
└─ wxt.config.ts
─ entrypoints/
├─ background.ts
├─ content.ts
├─ injected.ts
└─ popup.html
```
would result in the following `manifest.json`:
@@ -49,9 +48,9 @@ If a file uses a [special name recognized by WXT](/get-started/entrypoints.md),
- `content.ts` &rarr; `content_scripts.0.js.0`
- `background.ts` &rarr; `background.service_worker`
But not all entrypoints are added to the `manifest.json`. If they have a name that is not recognized by WXT, they are still built and included in the extension, but they are considered "unlisted" and are not apart of the manifest.
But not all entrypoints are added to the `manifest.json`. If the filename is not recognized by WXT, they are still built and included in the extension, but they are considered "unlisted" and are not apart of the manifest.
In this case, `injected.ts` gets bundled to `<outdir>/injected.js` and is accessible via `browser.runtime.getURL("/injected.js")`.
In this case, `injected.ts` gets output to `<outdir>/injected.js` and is accessible via `browser.runtime.getURL("/injected.js")`.
:::info
See [`/entrypoints` folder](/guide/background.md) documentation for a full list of recognized entrypoint filenames.
+26 -9
View File
@@ -1,9 +1,9 @@
# Installation
Bootstrap a new project or start from scratch.
Bootstrap a new project, start from scratch, or migrate an existing project.
:::warning 🚧&ensp;WSL Support
**_WXT does not support [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/) yet_**. See [Issue #55](https://github.com/aklinker1/wxt/issues/55) to track progress.
**_WXT does not support [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/) yet_**. See [Issue #55](https://github.com/wxt-dev/wxt/issues/55) to track progress.
In the meantime, you can use `cmd` instead.
:::
@@ -24,13 +24,13 @@ npx wxt@latest init <project-name>
There are several starting templates available.
| TypeScript |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <Icon name="TypeScript" /> [`vanilla`](https://github.com/aklinker1/wxt/tree/main/templates/vanilla) |
| <Icon name="Vue" /> [`vue`](https://github.com/aklinker1/wxt/tree/main/templates/vue) |
| <Icon name="React" /> [`react`](https://github.com/aklinker1/wxt/tree/main/templates/react) |
| <Icon name="Svelte" /> [`svelte`](https://github.com/aklinker1/wxt/tree/main/templates/svelte) |
| <Icon name="Solid" icon="https://www.solidjs.com/img/favicons/favicon-32x32.png" /> [`solid`](https://github.com/aklinker1/wxt/tree/main/templates/solid) |
| TypeScript |
| ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <Icon name="TypeScript" /> [`vanilla`](https://github.com/wxt-dev/wxt/tree/main/templates/vanilla) |
| <Icon name="Vue" /> [`vue`](https://github.com/wxt-dev/wxt/tree/main/templates/vue) |
| <Icon name="React" /> [`react`](https://github.com/wxt-dev/wxt/tree/main/templates/react) |
| <Icon name="Svelte" /> [`svelte`](https://github.com/wxt-dev/wxt/tree/main/templates/svelte) |
| <Icon name="Solid" icon="https://www.solidjs.com/img/favicons/favicon-32x32.png" /> [`solid`](https://github.com/wxt-dev/wxt/tree/main/templates/solid) |
> All templates are in TypeScript. WXT does not support JS at this time.
@@ -104,6 +104,22 @@ 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.
Migrating a project to WXT comes down to a few steps:
1. Install WXT and remove any old build tools: `pnpm i -D wxt`
1. Refactoring/move your entrypoints to the `entrypoints` directory
1. Moving public assets to the `public` directory
1. Update the dev and build scripts to use WXT
1. Ensure project is compatible with Vite, which is used under the hood to bundle your extension
:::info
Since projects vary greatly in setup, [start a discussion on GitHub](https://github.com/wxt-dev/wxt/discussions/new/choose) if you need help migrating your project to WXT.
:::
## Development
Once you've installed WXT, you can start the development server using the `dev` script.
@@ -134,3 +150,4 @@ You're ready to build your web extension!
- Learn how to [add entrypoints](./entrypoints.md) like the popup, options page, or content scripts
- [Configure WXT](./configuration.md) by creating a `wxt.config.ts` file
- Checkout [example projects](https://github.com/wxt-dev/wxt-examples) to see how to perfom common tasks with WXT
+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)
+3 -3
View File
@@ -16,9 +16,9 @@ wxt zip -b firefox
Generated ZIP files are stored in the `.output` directory.
## Setup Automated Submissions
## Automation
To automate submissions, use the [`publish-browser-extension`](https://www.npmjs.com/package/publish-browser-extension) package.
To automate releasing updates, use the [`publish-browser-extension`](https://www.npmjs.com/package/publish-browser-extension) package.
:::info
🚧 WXT plans to eventually incorporate the `publish-browser-extension` package into its own `wxt submit` command.
@@ -159,7 +159,7 @@ Ensure that you have a `README.md` or `SOURCE_CODE_REVIEW.md` file with the abov
🚧 Not automated at this time
:::warning
🚧 WXT does not currently support automated publishing for Safari. Safari extensions require a native MacOS or iOS app wrapper, which WXT isn't configured to create. For now, if you want to publish to Safari, follow this guide:
🚧 WXT does not currently support automated publishing for Safari. Safari extensions require a native MacOS or iOS app wrapper, which WXT cannot create at this time. For now, if you want to publish to Safari, follow this guide:
https://developer.apple.com/documentation/safariservices/safari_web_extensions/distributing_your_safari_web_extension
+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
+5
View File
@@ -9,6 +9,7 @@ For MV2, the background is added as a script to the background page. For MV3, th
<EntrypointPatterns
:patterns="[
['background.ts', 'background.js'],
['background/index.ts', 'background.js'],
]"
/>
@@ -32,6 +33,10 @@ export default defineBackground({
persistent: undefined | true | false,
type: undefined | 'module',
// Set include/exclude if the background should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
// Executed when background is loaded
main() {
// ...
+4 -3
View File
@@ -13,15 +13,16 @@
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+9 -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'],
]"
/>
@@ -30,6 +30,10 @@ export default defineContentScript({
matchOriginAsFallback: undefined | true | false,
world: undefined | 'ISOLATED' | 'MAIN',
// Set include/exclude if the background should be removed from some builds
include: undefined | string[],
exclude: undefined | string[],
main() {
// Executed when content script is loaded
},
+4 -1
View File
@@ -16,12 +16,15 @@
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+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):
+4 -3
View File
@@ -13,15 +13,16 @@
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+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).
+4 -3
View File
@@ -13,15 +13,16 @@
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+4 -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" />
@@ -25,6 +23,9 @@ Plain old HTML file.
<meta name="manifest.open_in_tab" content="true|false" />
<meta name="manifest.chrome_style" content="true|false" />
<meta name="manifest.browser_style" content="true|false" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+4 -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,6 +29,9 @@ Plain old HTML file.
}"
/>
<meta name="manifest.type" content="page_action|browser_action" />
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+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',
});
```
+4 -3
View File
@@ -19,15 +19,16 @@ Firefox does not support sandboxed pages.
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+4 -3
View File
@@ -19,15 +19,16 @@ Firefox does not support sidepanel pages.
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+4 -3
View File
@@ -29,15 +29,16 @@ console.log(url); // "chrome-extension://<id>/<name>.html"
## Definition
Plain old HTML file.
```html
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
<!-- Set include/exclude if the page should be removed from some builds -->
<meta name="manifest.include" content="['chrome', ...]" />
<meta name="manifest.exclude" content="['chrome', ...]" />
</head>
<body>
<!-- ... -->
+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'],
]"
/>
+35
View File
@@ -0,0 +1,35 @@
# Vite
Under the hood, WXT uses Vite to bundle your web extension.
## Basic Vite Configuration
All of Vite's config can be customized by setting the `vite` configuration in your `wxt.config.ts` file.
```ts
import { defineConfig } from 'wxt';
export default defineConfig({
vite: {
// Same as `defineConfig({ ... })` inside vite.config.ts
},
});
```
## Using Plugins
Plugins can be passed into the `vite` configuration in you `wxt.config.ts` file, just like any other option.
All plugins should work in WXT, but it is worth pointing out that since WXT orchestrates multiple vite builds to bundle an extension, plugins will be executed multiple times if necessary.
```ts
import { defineConfig } from 'wxt';
export default defineConfig({
vite: {
plugins: [
// ...
],
},
});
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

After

Width:  |  Height:  |  Size: 472 KiB

+69 -42
View File
@@ -2,60 +2,87 @@ import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
describe('Auto Imports', () => {
it('should output types for entrypoint paths', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/background.ts',
'export default defineBackground(() => {})',
);
project.addFile(
'entrypoints/overlay.content.ts',
'export default defineContentScript(() => {})',
);
project.addFile('entrypoints/popup.html', '<html></html>');
describe('imports: { ... }', () => {
it('should generate a declaration file, imports.d.ts, for auto-imports', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html', `<html></html>`);
await project.build();
await project.build();
expect(await project.serializeFile('.wxt/types/paths.d.ts'))
.toMatchInlineSnapshot(`
".wxt/types/paths.d.ts
expect(await project.serializeFile('.wxt/types/imports.d.ts'))
.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 include auto-imports in the project', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html', `<html></html>`);
await project.build();
expect(await project.serializeFile('.wxt/wxt.d.ts'))
.toMatchInlineSnapshot(`
".wxt/wxt.d.ts
----------------------------------------
// Generated by wxt
import \\"wxt/browser\\";
declare module \\"wxt/browser\\" {
type PublicPath =
| \\"/background.js\\"
| \\"/content-scripts/overlay.js\\"
| \\"/popup.html\\"
export interface ProjectRuntime extends Runtime.Static {
getURL(path: PublicPath): string;
}
}
/// <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 make some client utils auto-importable', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html', `<html></html>`);
describe('imports: false', () => {
it('should not generate a imports.d.ts file', async () => {
const project = new TestProject();
project.setConfigFileConfig({
imports: false,
});
project.addFile('entrypoints/popup.html', `<html></html>`);
await project.build();
await project.build();
expect(await project.serializeFile('.wxt/types/imports.d.ts'))
.toMatchInlineSnapshot(`
".wxt/types/imports.d.ts
expect(await project.fileExists('.wxt/types/imports.d.ts')).toBe(false);
});
it('should not include imports.d.ts in the type references', async () => {
const project = new TestProject();
project.setConfigFileConfig({
imports: false,
});
project.addFile('entrypoints/popup.html', `<html></html>`);
await project.build();
expect(
await project.serializeFile('.wxt/wxt.d.ts'),
).toMatchInlineSnapshot(
`
".wxt/wxt.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']
}
/// <reference types=\\"vite/client\\" />
/// <reference types=\\"./types/paths.d.ts\\" />
/// <reference types=\\"./types/i18n.d.ts\\" />
/// <reference types=\\"./types/globals.d.ts\\" />
"
`);
`,
);
});
});
});
+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'],
});
});
});
+40
View File
@@ -93,4 +93,44 @@ describe('Output Directory Structure', () => {
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"*://*/*\\"],\\"js\\":[\\"content-scripts/overlay-one.js\\"]}]}"
`);
});
it('should not include an entrypoint if the target browser is not in the list of included targets', async () => {
const project = new TestProject();
project.addFile('entrypoints/options.html', '<html></html>');
project.addFile(
'entrypoints/background.ts',
`
export default defineBackground({
include: ["chrome"],
main() {},
})
`,
);
await project.build({ browser: 'firefox' });
expect(await project.fileExists('.output/firefox-mv2/background.js')).toBe(
false,
);
});
it('should not include an entrypoint if the target browser is in the list of excluded targets', async () => {
const project = new TestProject();
project.addFile('entrypoints/options.html', '<html></html>');
project.addFile(
'entrypoints/background.ts',
`
export default defineBackground({
exclude: ["chrome"],
main() {},
})
`,
);
await project.build({ browser: 'chrome' });
expect(await project.fileExists('.output/firefox-mv2/background.js')).toBe(
false,
);
});
});
+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(
+319
View File
@@ -0,0 +1,319 @@
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 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 generate 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);
+30 -27
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.3.1",
"version": "0.5.5",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18.16.0",
@@ -9,8 +9,9 @@
},
"repository": {
"type": "git",
"url": "https://github.com/aklinker1/wxt"
"url": "https://github.com/wxt-dev/wxt"
},
"homepage": "https://wxt.dev",
"keywords": [
"vite",
"chrome",
@@ -60,31 +61,31 @@
"prepublish": "pnpm -s build",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
"docs:preview": "vitepress preview docs",
"sync-releases": "pnpx changelogen@latest gh release all"
},
"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 +93,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 +118,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"
]
}
}
+645 -2044
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;
+3 -2
View File
@@ -10,16 +10,17 @@ export const build = defineCommand<
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
const mode = flags.mode ?? 'production';
const cliConfig: wxt.InlineConfig = {
root,
mode,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
await wxt.build(cliConfig);
+11
View File
@@ -0,0 +1,11 @@
import { defineCommand } from '../utils/defineCommand';
import * as wxt from '../..';
export const clean = defineCommand<
[
root: string | undefined,
flags: {
debug?: boolean;
},
]
>(wxt.clean);
+3 -2
View File
@@ -10,16 +10,17 @@ export const dev = defineCommand<
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
const mode = flags.mode ?? 'development';
const cliConfig: wxt.InlineConfig = {
root,
mode,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
const server = await wxt.createServer(cliConfig);
+1
View File
@@ -4,3 +4,4 @@ export * from './init';
export * from './prepare';
export * from './publish';
export * from './zip';
export * from './clean';
+7 -4
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');
@@ -69,7 +72,7 @@ export const init = defineCommand<
console.log();
consola.log('Next steps:');
let step = 0;
if (cdPath !== '.') consola.log(` ${++step}.`, pc.cyan(`cd ${cdPath}`));
if (cdPath !== '') consola.log(` ${++step}.`, pc.cyan(`cd ${cdPath}`));
consola.log(` ${++step}.`, pc.cyan(`${input.packageManager} install`));
console.log();
},
@@ -90,7 +93,7 @@ interface Template {
async function listTemplates(): Promise<Template[]> {
try {
const res = await fetch(
'https://api.github.com/repos/aklinker1/wxt/contents/templates',
'https://api.github.com/repos/wxt-dev/wxt/contents/templates',
{
headers: {
Accept: 'application/vnd.github+json',
@@ -133,7 +136,7 @@ async function cloneProject({
const spinner = ora('Downloading template').start();
try {
// 1. Clone repo
await downloadTemplate(`gh:aklinker1/wxt/${template.path}`, {
await downloadTemplate(`gh:wxt-dev/wxt/${template.path}`, {
dir: directory,
force: true,
});
+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');
},
);
+3 -2
View File
@@ -13,16 +13,17 @@ export const zip = defineCommand<
browser?: wxt.TargetBrowser;
mv3?: boolean;
mv2?: boolean;
debug?: boolean;
},
]
>(async (root, flags) => {
const mode = flags.mode ?? 'production';
const cliConfig: wxt.InlineConfig = {
root,
mode,
mode: flags.mode,
browser: flags.browser,
manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : undefined,
configFile: flags.config,
debug: flags.debug,
};
const config = await getInternalConfig(cliConfig, 'build');
+9 -1
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')
@@ -38,10 +40,16 @@ cli
// PREPARE
cli
.command('prepare [root]', 'prepare')
.command('prepare [root]', 'prepare typescript project')
.option('-c, --config <file>', 'use specified config file')
.action(commands.prepare);
// CLEAN
cli
.command('clean [root]', 'clean generated files and caches')
.alias('cleanup')
.action(commands.clean);
// PUBLISH
cli.command('publish [root]', 'publish to stores').action(commands.publish);
+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
}
+5 -2
View File
@@ -38,14 +38,17 @@ export async function buildInternal(
await fs.ensureDir(config.outDir);
const entrypoints = await findEntrypoints(config);
config.logger.debug('Detected entrypoints:', entrypoints);
const groups = groupEntrypoints(entrypoints);
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;
}
@@ -9,13 +9,13 @@ import {
import { resolve } from 'path';
import { findEntrypoints } from '../findEntrypoints';
import fs from 'fs-extra';
import { importTsFile } from '../../utils/importTsFile';
import { importEntrypointFile } from '../../utils/importEntrypointFile';
import glob from 'fast-glob';
import { fakeInternalConfig } from '../../../testing/fake-objects';
import { unnormalizePath } from '../../utils/paths';
vi.mock('../../utils/importTsFile');
const importTsFileMock = vi.mocked(importTsFile);
vi.mock('../../utils/importEntrypointFile');
const importEntrypointFileMock = vi.mocked(importEntrypointFile);
vi.mock('fast-glob');
const globMock = vi.mocked(glob);
@@ -191,13 +191,16 @@ describe('findEntrypoints', () => {
matches: ['<all_urls>'],
};
globMock.mockResolvedValueOnce([path]);
importTsFileMock.mockResolvedValue(options);
importEntrypointFileMock.mockResolvedValue(options);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toHaveLength(1);
expect(entrypoints[0]).toEqual({ ...expected, options });
expect(importTsFileMock).toBeCalledWith(expected.inputPath, config);
expect(importEntrypointFileMock).toBeCalledWith(
expected.inputPath,
config,
);
},
);
@@ -211,6 +214,15 @@ describe('findEntrypoints', () => {
outputDir: config.outDir,
},
],
[
'background/index.ts',
{
type: 'background',
name: 'background',
inputPath: resolve(config.entrypointsDir, 'background/index.ts'),
outputDir: config.outDir,
},
],
])(
'should find and load background entrypoint config from %s',
async (path, expected) => {
@@ -218,13 +230,16 @@ describe('findEntrypoints', () => {
type: 'module',
};
globMock.mockResolvedValueOnce([path]);
importTsFileMock.mockResolvedValue(options);
importEntrypointFileMock.mockResolvedValue(options);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toHaveLength(1);
expect(entrypoints[0]).toEqual({ ...expected, options });
expect(importTsFileMock).toBeCalledWith(expected.inputPath, config);
expect(importEntrypointFileMock).toBeCalledWith(
expected.inputPath,
config,
);
},
);
@@ -255,6 +270,7 @@ describe('findEntrypoints', () => {
name: 'sandbox',
inputPath: resolve(config.entrypointsDir, 'sandbox.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -264,6 +280,7 @@ describe('findEntrypoints', () => {
name: 'sandbox',
inputPath: resolve(config.entrypointsDir, 'sandbox/index.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -273,6 +290,7 @@ describe('findEntrypoints', () => {
name: 'named',
inputPath: resolve(config.entrypointsDir, 'named.sandbox.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -282,6 +300,7 @@ describe('findEntrypoints', () => {
name: 'named',
inputPath: resolve(config.entrypointsDir, 'named.sandbox/index.html'),
outputDir: config.outDir,
options: {},
},
],
@@ -293,6 +312,7 @@ describe('findEntrypoints', () => {
name: 'bookmarks',
inputPath: resolve(config.entrypointsDir, 'bookmarks.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -302,6 +322,7 @@ describe('findEntrypoints', () => {
name: 'bookmarks',
inputPath: resolve(config.entrypointsDir, 'bookmarks/index.html'),
outputDir: config.outDir,
options: {},
},
],
@@ -313,6 +334,7 @@ describe('findEntrypoints', () => {
name: 'history',
inputPath: resolve(config.entrypointsDir, 'history.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -322,6 +344,7 @@ describe('findEntrypoints', () => {
name: 'history',
inputPath: resolve(config.entrypointsDir, 'history/index.html'),
outputDir: config.outDir,
options: {},
},
],
@@ -333,6 +356,7 @@ describe('findEntrypoints', () => {
name: 'newtab',
inputPath: resolve(config.entrypointsDir, 'newtab.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -342,6 +366,7 @@ describe('findEntrypoints', () => {
name: 'newtab',
inputPath: resolve(config.entrypointsDir, 'newtab/index.html'),
outputDir: config.outDir,
options: {},
},
],
@@ -353,6 +378,7 @@ describe('findEntrypoints', () => {
name: 'sidepanel',
inputPath: resolve(config.entrypointsDir, 'sidepanel.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -362,6 +388,7 @@ describe('findEntrypoints', () => {
name: 'sidepanel',
inputPath: resolve(config.entrypointsDir, 'sidepanel/index.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -371,6 +398,7 @@ describe('findEntrypoints', () => {
name: 'named',
inputPath: resolve(config.entrypointsDir, 'named.sidepanel.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -380,6 +408,7 @@ describe('findEntrypoints', () => {
name: 'named',
inputPath: resolve(config.entrypointsDir, 'named.sidepanel/index.html'),
outputDir: config.outDir,
options: {},
},
],
@@ -391,6 +420,7 @@ describe('findEntrypoints', () => {
name: 'devtools',
inputPath: resolve(config.entrypointsDir, 'devtools.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -400,6 +430,7 @@ describe('findEntrypoints', () => {
name: 'devtools',
inputPath: resolve(config.entrypointsDir, 'devtools/index.html'),
outputDir: config.outDir,
options: {},
},
],
@@ -411,6 +442,7 @@ describe('findEntrypoints', () => {
name: 'onboarding',
inputPath: resolve(config.entrypointsDir, 'onboarding.html'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -420,6 +452,7 @@ describe('findEntrypoints', () => {
name: 'onboarding',
inputPath: resolve(config.entrypointsDir, 'onboarding/index.html'),
outputDir: config.outDir,
options: {},
},
],
@@ -431,6 +464,7 @@ describe('findEntrypoints', () => {
name: 'injected',
inputPath: resolve(config.entrypointsDir, 'injected.ts'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -440,6 +474,7 @@ describe('findEntrypoints', () => {
name: 'injected',
inputPath: resolve(config.entrypointsDir, 'injected/index.ts'),
outputDir: config.outDir,
options: {},
},
],
@@ -451,6 +486,7 @@ describe('findEntrypoints', () => {
name: 'iframe',
inputPath: resolve(config.entrypointsDir, 'iframe.scss'),
outputDir: config.outDir,
options: {},
},
],
[
@@ -460,6 +496,7 @@ describe('findEntrypoints', () => {
name: 'iframe',
inputPath: resolve(config.entrypointsDir, 'iframe.css'),
outputDir: config.outDir,
options: {},
},
],
@@ -471,6 +508,7 @@ describe('findEntrypoints', () => {
name: 'content',
inputPath: resolve(config.entrypointsDir, 'content.css'),
outputDir: resolve(config.outDir, 'content-scripts'),
options: {},
},
],
[
@@ -480,6 +518,7 @@ describe('findEntrypoints', () => {
name: 'overlay',
inputPath: resolve(config.entrypointsDir, 'overlay.content.css'),
outputDir: resolve(config.outDir, 'content-scripts'),
options: {},
},
],
[
@@ -489,6 +528,7 @@ describe('findEntrypoints', () => {
name: 'content',
inputPath: resolve(config.entrypointsDir, 'content/index.css'),
outputDir: resolve(config.outDir, 'content-scripts'),
options: {},
},
],
[
@@ -498,6 +538,7 @@ describe('findEntrypoints', () => {
name: 'overlay',
inputPath: resolve(config.entrypointsDir, 'overlay.content/index.css'),
outputDir: resolve(config.outDir, 'content-scripts'),
options: {},
},
],
])('should find entrypoint for %s', async (path, expected) => {
@@ -521,4 +562,148 @@ describe('findEntrypoints', () => {
expectedPaths.join(', '),
);
});
describe('include option', () => {
it("should filter out the background when include doesn't contain the target browser", async () => {
globMock.mockResolvedValueOnce(['background.ts']);
importEntrypointFileMock.mockResolvedValue({
include: ['not' + config.browser],
});
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it("should filter out content scripts when include doesn't contain the target browser", async () => {
globMock.mockResolvedValueOnce(['example.content.ts']);
importEntrypointFileMock.mockResolvedValue({
include: ['not' + config.browser],
});
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it("should filter out the popup when include doesn't contain the target browser", async () => {
globMock.mockResolvedValueOnce(['popup.html']);
readFileMock.mockResolvedValueOnce(
`<html>
<head>
<meta name="manifest.include" content="['${
'not' + config.browser
}']" />
</head>
</html>`,
);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it("should filter out the options page when include doesn't contain the target browser", async () => {
globMock.mockResolvedValueOnce(['options.html']);
readFileMock.mockResolvedValueOnce(
`<html>
<head>
<meta name="manifest.include" content="['${
'not' + config.browser
}']" />
</head>
</html>`,
);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it("should filter out an unlisted page when include doesn't contain the target browser", async () => {
globMock.mockResolvedValueOnce(['unlisted.html']);
readFileMock.mockResolvedValueOnce(
`<html>
<head>
<meta name="manifest.include" content="['${
'not' + config.browser
}']" />
</head>
</html>`,
);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
});
describe('exclude option', () => {
it('should filter out the background when exclude contains the target browser', async () => {
globMock.mockResolvedValueOnce(['background.ts']);
importEntrypointFileMock.mockResolvedValue({
exclude: [config.browser],
});
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it('should filter out content scripts when exclude contains the target browser', async () => {
globMock.mockResolvedValueOnce(['example.content.ts']);
importEntrypointFileMock.mockResolvedValue({
exclude: [config.browser],
});
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it('should filter out the popup when exclude contains the target browser', async () => {
globMock.mockResolvedValueOnce(['popup.html']);
readFileMock.mockResolvedValueOnce(
`<html>
<head>
<meta name="manifest.exclude" content="['${config.browser}']" />
</head>
</html>`,
);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it('should filter out the options page when exclude contains the target browser', async () => {
globMock.mockResolvedValueOnce(['options.html']);
readFileMock.mockResolvedValueOnce(
`<html>
<head>
<meta name="manifest.exclude" content="['${config.browser}']" />
</head>
</html>`,
);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
it('should filter out an unlisted page when exclude contains the target browser', async () => {
globMock.mockResolvedValueOnce(['unlisted.html']);
readFileMock.mockResolvedValueOnce(
`<html>
<head>
<meta name="manifest.exclude" content="['${config.browser}']" />
</head>
</html>`,
);
const entrypoints = await findEntrypoints(config);
expect(entrypoints).toEqual([]);
});
});
});
+4
View File
@@ -78,6 +78,10 @@ async function buildSingleEntrypoint(
},
},
},
define: {
// See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
'process.env.NODE_ENV': JSON.stringify(config.mode),
},
};
const entryConfig = vite.mergeConfig(
libMode,
+86 -16
View File
@@ -2,9 +2,11 @@ import { relative, resolve } from 'path';
import {
BackgroundEntrypoint,
BackgroundScriptDefintition,
BaseEntrypointOptions,
ContentScriptDefinition,
ContentScriptEntrypoint,
Entrypoint,
GenericEntrypoint,
InternalConfig,
OptionsEntrypoint,
PopupEntrypoint,
@@ -13,7 +15,7 @@ import fs from 'fs-extra';
import { minimatch } from 'minimatch';
import { parseHTML } from 'linkedom';
import JSON5 from 'json5';
import { importTsFile } from '../utils/importTsFile';
import { importEntrypointFile } from '../utils/importEntrypointFile';
import glob from 'fast-glob';
import { getEntrypointName } from '../utils/entrypoints';
import { VIRTUAL_NOOP_BACKGROUND_MODULE_ID } from '../vite-plugins/noopBackground';
@@ -75,12 +77,19 @@ export async function findEntrypoints(
path,
);
break;
case 'unlisted-page':
entrypoint = await getUnlistedPageEntrypoint(config, path);
break;
case 'content-script-style':
entrypoint = {
type,
name: getEntrypointName(config.entrypointsDir, path),
inputPath: path,
outputDir: resolve(config.outDir, CONTENT_SCRIPT_OUT_DIR),
options: {
include: undefined,
exclude: undefined,
},
};
break;
default:
@@ -89,6 +98,10 @@ export async function findEntrypoints(
name: getEntrypointName(config.entrypointsDir, path),
inputPath: path,
outputDir: config.outDir,
options: {
include: undefined,
exclude: undefined,
},
};
}
@@ -112,7 +125,47 @@ export async function findEntrypoints(
await getBackgroundEntrypoint(config, VIRTUAL_NOOP_BACKGROUND_MODULE_ID),
);
}
return entrypoints;
config.logger.debug('All entrypoints:', entrypoints);
const targetEntrypoints = entrypoints.filter((entry) => {
const { include, exclude } = entry.options;
if (include?.length && exclude?.length) {
config.logger.warn(
`The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`,
);
return false;
}
if (exclude?.length && !include?.length) {
return !exclude.includes(config.browser);
}
if (include?.length && !exclude?.length) {
return include.includes(config.browser);
}
return true;
});
config.logger.debug(`${config.browser} entrypoints:`, targetEntrypoints);
return targetEntrypoints;
}
function getHtmlBaseOptions(document: Document): BaseEntrypointOptions {
const options: BaseEntrypointOptions = {};
const includeContent = document
.querySelector("meta[name='manifest.include']")
?.getAttribute('content');
if (includeContent) {
options.include = JSON5.parse(includeContent);
}
const excludeContent = document
.querySelector("meta[name='manifest.exclude']")
?.getAttribute('content');
if (excludeContent) {
options.exclude = JSON5.parse(excludeContent);
}
return options;
}
/**
@@ -123,11 +176,11 @@ async function getPopupEntrypoint(
config: InternalConfig,
path: string,
): Promise<PopupEntrypoint> {
const options: PopupEntrypoint['options'] = {};
const content = await fs.readFile(path, 'utf-8');
const { document } = parseHTML(content);
const options: PopupEntrypoint['options'] = getHtmlBaseOptions(document);
const title = document.querySelector('title');
if (title != null) options.defaultTitle = title.textContent ?? undefined;
@@ -170,11 +223,11 @@ async function getOptionsEntrypoint(
config: InternalConfig,
path: string,
): Promise<OptionsEntrypoint> {
const options: OptionsEntrypoint['options'] = {};
const content = await fs.readFile(path, 'utf-8');
const { document } = parseHTML(content);
const options: OptionsEntrypoint['options'] = getHtmlBaseOptions(document);
const openInTabContent = document
.querySelector("meta[name='manifest.open_in_tab']")
?.getAttribute('content');
@@ -205,6 +258,26 @@ async function getOptionsEntrypoint(
};
}
/**
* @param path Absolute path to the HTML file.
* @param content String contents of the file at the path.
*/
async function getUnlistedPageEntrypoint(
config: InternalConfig,
path: string,
): Promise<GenericEntrypoint> {
const content = await fs.readFile(path, 'utf-8');
const { document } = parseHTML(content);
return {
type: 'unlisted-page',
name: getEntrypointName(config.entrypointsDir, path),
inputPath: path,
outputDir: config.outDir,
options: getHtmlBaseOptions(document),
};
}
/**
* @param path Absolute path to the background's TS file.
*/
@@ -214,10 +287,8 @@ async function getBackgroundEntrypoint(
): Promise<BackgroundEntrypoint> {
let options: Omit<BackgroundScriptDefintition, 'main'> = {};
if (path !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
const defaultExport = await importTsFile<BackgroundScriptDefintition>(
path,
config,
);
const defaultExport =
await importEntrypointFile<BackgroundScriptDefintition>(path, config);
if (defaultExport == null) {
throw Error('Background script does not have a default export');
}
@@ -241,10 +312,8 @@ async function getContentScriptEntrypoint(
name: string,
path: string,
): Promise<ContentScriptEntrypoint> {
const { main: _, ...options } = await importTsFile<ContentScriptDefinition>(
path,
config,
);
const { main: _, ...options } =
await importEntrypointFile<ContentScriptDefinition>(path, config);
if (options == null) {
throw Error(`Content script ${name} does not have a default export`);
}
@@ -281,6 +350,7 @@ const PATH_GLOB_TO_TYPE_MAP: Record<string, Entrypoint['type'] | 'ignored'> = {
'devtools/index.html': 'devtools',
'background.ts': 'background',
'background/index.ts': 'background',
[VIRTUAL_NOOP_BACKGROUND_MODULE_ID]: 'background',
'content.ts?(x)': 'content-script',
@@ -305,8 +375,8 @@ const PATH_GLOB_TO_TYPE_MAP: Record<string, Entrypoint['type'] | 'ignored'> = {
[`*.${CSS_EXTENSIONS_PATTERN}`]: 'unlisted-style',
[`*/index.${CSS_EXTENSIONS_PATTERN}`]: 'unlisted-style',
// Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files
'*/*': 'ignored',
// Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files or tests
'*/**': 'ignored',
};
const CONTENT_SCRIPT_OUT_DIR = 'content-scripts';
+88 -19
View File
@@ -1,4 +1,4 @@
import { createUnimport } from 'unimport';
import { UnimportOptions, createUnimport } from 'unimport';
import { Entrypoint, InternalConfig } from '../types';
import fs from 'fs-extra';
import { relative, resolve } from 'path';
@@ -7,6 +7,9 @@ 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';
import { writeFileIfDifferent } from '../utils/fs';
/**
* Generate and write all the files inside the `InternalConfig.typesDir` directory.
@@ -18,8 +21,14 @@ export async function generateTypesDir(
await fs.ensureDir(config.typesDir);
const references: string[] = [];
references.push(await writeImportsDeclarationFile(config));
const imports = getUnimportOptions(config);
if (imports !== false) {
references.push(await writeImportsDeclarationFile(config, imports));
}
references.push(await writePathsDeclarationFile(entrypoints, config));
references.push(await writeI18nDeclarationFile(config));
references.push(await writeGlobalsDeclarationFile(config));
const mainReference = await writeMainDeclarationFile(references, config);
@@ -28,14 +37,15 @@ export async function generateTypesDir(
async function writeImportsDeclarationFile(
config: InternalConfig,
unimportOptions: Partial<UnimportOptions>,
): Promise<string> {
const filePath = resolve(config.typesDir, 'imports.d.ts');
const unimport = createUnimport(getUnimportOptions(config));
const unimport = createUnimport(unimportOptions);
// Load project imports into unimport memory so they are output via generateTypeDeclarations
await unimport.scanImportsFromDir(undefined, { cwd: config.srcDir });
await fs.writeFile(
await writeFileIfDifferent(
filePath,
['// Generated by wxt', await unimport.generateTypeDeclarations()].join(
'\n',
@@ -70,13 +80,13 @@ 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;
}
}
`;
await fs.writeFile(
await writeFileIfDifferent(
filePath,
template.replace('{{ union }}', unions || ' | never'),
);
@@ -84,12 +94,71 @@ 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 writeFileIfDifferent(
filePath,
template.replace('{{ overrides }}', overrides.join('\n')),
);
return filePath;
}
async function writeGlobalsDeclarationFile(
config: InternalConfig,
): Promise<string> {
const filePath = resolve(config.typesDir, 'globals.d.ts');
const globals = getGlobals(config);
await fs.writeFile(
await writeFileIfDifferent(
filePath,
[
'// Generated by wxt',
@@ -98,7 +167,6 @@ async function writeGlobalsDeclarationFile(
...globals.map((global) => ` const ${global.name}: ${global.type};`),
'}',
].join('\n') + '\n',
'utf-8',
);
return filePath;
}
@@ -109,7 +177,7 @@ async function writeMainDeclarationFile(
): Promise<string> {
const dir = config.wxtDir;
const filePath = resolve(dir, 'wxt.d.ts');
await fs.writeFile(
await writeFileIfDifferent(
filePath,
[
'// Generated by wxt',
@@ -128,7 +196,9 @@ async function writeTsConfigFile(
config: InternalConfig,
) {
const dir = config.wxtDir;
await fs.writeFile(
const rootPath = normalizePath(relative(dir, config.root));
const srcPath = normalizePath(relative(dir, config.srcDir));
await writeFileIfDifferent(
resolve(dir, 'tsconfig.json'),
`{
"compilerOptions": {
@@ -142,16 +212,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": [
+41
View File
@@ -0,0 +1,41 @@
import path from 'node:path';
import glob from 'fast-glob';
import fs from 'fs-extra';
import { consola } from 'consola';
import pc from 'picocolors';
/**
* Remove generated/temp files from the directory.
*
* @param root The directory to look for generated/temp files in. Defaults to `process.cwd()`. Can be relative to `process.cwd()` or absolute.
*/
export async function clean(root = process.cwd()) {
consola.info('Cleaning Project');
const tempDirs = [
'node_modules/.vite',
'node_modules/.cache',
'**/.wxt',
'.output/*',
];
consola.debug('Looking for:', tempDirs.map(pc.cyan).join(', '));
const directories = await glob(tempDirs, {
cwd: path.resolve(root),
absolute: true,
onlyDirectories: true,
deep: 2,
});
if (directories.length === 0) {
consola.debug('No generated files found.');
return;
}
consola.debug(
'Found:',
directories.map((dir) => pc.cyan(path.relative(root, dir))).join(', '),
);
for (const directory of directories) {
await fs.rm(directory, { force: true, recursive: true });
consola.debug('Deleted ' + pc.cyan(path.relative(root, directory)));
}
}
+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);
},
+88 -72
View File
@@ -2,61 +2,68 @@ 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;
/**
* Path to `"wxt.config.ts"` file or false to disable config file discovery.
* > Only available when using the JS API. Not available in `wxt.config.ts` files
*
* @default
* "wxt.config.ts"
* Path to `wxt.config.ts` file or `false` to disable config file discovery.
*
* @default "wxt.config.ts"
*/
configFile?: string | false;
/**
* ID of the extension for each store. Used for publishing.
* Set to `true` to show debug logs. Overriden by the command line `--debug` option.
*
* @default false
*/
storeIds?: {
chrome?: string;
firefox?: string;
edge?: string;
};
debug?: boolean;
/**
* Explicitly set a mode to run in. This will override the default mode for each command, and can
* be overridden by the command line `--mode` option.
*/
mode?: string;
/**
* Customize auto-import options.
* Customize auto-import options. Set to `false` to disable auto-imports.
*
* For example, to add a directory to auto-import from, you can use:
*
* ```ts
* export default defineConfig({
* imports: {
* dirs: ["some-directory"]
* }
* })
* ```
*/
imports?: Partial<UnimportOptions>;
imports?: Partial<UnimportOptions> | false;
/**
* 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
@@ -76,18 +83,18 @@ export interface InlineConfig {
*/
logger?: Logger;
/**
* Custom Vite options.
* Custom Vite options, see <https://vitejs.dev/config/shared-options.html>.
*
* [`root`](#root), [`configFile`](#configfile), and [`mode`](#mode) should be set in WXT's config.
*/
vite?: Omit<vite.UserConfig, 'root' | 'configFile' | 'mode'>;
vite?:
| WxtViteConfig
| ((env: ConfigEnv) => WxtViteConfig | Promise<WxtViteConfig>);
/**
* Customize the `manifest.json` output. Can be an object, promise, or function that returns an
* object or promise.
*/
manifest?: UserManifest | Promise<UserManifest> | UserManifestFn;
/**
* Custom server options.
*/
server?: WxtDevServer;
/**
* Custom runner options. Options set here can be overridden in a `web-ext.config.ts` file.
*/
@@ -98,12 +105,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 +118,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;
/**
@@ -208,7 +216,7 @@ export interface WxtDevServer extends vite.ViteDevServer {
) => void;
}
export type TargetBrowser = 'chrome' | 'firefox' | 'safari' | 'edge' | 'opera';
export type TargetBrowser = string;
export type TargetManifestVersion = 2 | 3;
export type UserConfig = Omit<InlineConfig, 'configFile'>;
@@ -221,6 +229,12 @@ export interface Logger {
error(...args: any[]): void;
fatal(...args: any[]): void;
success(...args: any[]): void;
level: LogLevel;
}
export interface BaseEntrypointOptions {
include?: TargetBrowser[];
exclude?: TargetBrowser[];
}
export interface BaseEntrypoint {
@@ -250,6 +264,7 @@ export interface BaseEntrypoint {
* subdirectory of it.
*/
outputDir: string;
options: BaseEntrypointOptions;
}
export interface GenericEntrypoint extends BaseEntrypoint {
@@ -271,12 +286,12 @@ export interface BackgroundEntrypoint extends BaseEntrypoint {
options: {
persistent?: boolean;
type?: 'module';
};
} & BaseEntrypointOptions;
}
export interface ContentScriptEntrypoint extends BaseEntrypoint {
type: 'content-script';
options: Omit<ContentScriptDefinition, 'main'>;
options: Omit<ContentScriptDefinition, 'main'> & BaseEntrypointOptions;
}
export interface PopupEntrypoint extends BaseEntrypoint {
@@ -288,7 +303,7 @@ export interface PopupEntrypoint extends BaseEntrypoint {
mv2Key?: 'browser_action' | 'page_action';
defaultIcon?: Record<string, string>;
defaultTitle?: string;
};
} & BaseEntrypointOptions;
}
export interface OptionsEntrypoint extends BaseEntrypoint {
@@ -297,7 +312,7 @@ export interface OptionsEntrypoint extends BaseEntrypoint {
openInTab?: boolean;
browserStyle?: boolean;
chromeStyle?: boolean;
};
} & BaseEntrypointOptions;
}
export type Entrypoint =
@@ -309,7 +324,7 @@ export type Entrypoint =
export type OnContentScriptStopped = (cb: () => void) => void;
export interface ContentScriptDefinition {
export interface ContentScriptDefinition extends ExcludableEntrypoint {
matches: Manifest.ContentScript['matches'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
@@ -357,11 +372,28 @@ export interface ContentScriptDefinition {
main(): void | Promise<void>;
}
export interface BackgroundScriptDefintition {
export interface BackgroundScriptDefintition extends ExcludableEntrypoint {
type?: 'module';
main(): void;
}
export interface ExcludableEntrypoint {
/**
* List of target browsers to include this entrypoint in. Defaults to being included in all
* builds. Cannot be used with `exclude`. You must choose one of the two options.
*
* @default undefined
*/
include?: TargetBrowser[];
/**
* List of target browsers to exclude this entrypoint from. Cannot be used with `include`. You
* must choose one of the two options.
*
* @default undefined
*/
exclude?: TargetBrowser[];
}
/**
* Manifest customization available in the `wxt.config.ts` file. You cannot configure entrypoints
* here, they are configured inline.
@@ -373,7 +405,6 @@ export type UserManifest = Partial<
| 'background'
| 'browser_action'
| 'chrome_url_overrides'
| 'content_scripts'
| 'devtools_page'
| 'manifest_version'
| 'options_page'
@@ -417,31 +448,11 @@ export interface ExtensionRunnerConfig {
openDevtools?: boolean;
/**
* List of browser names and the binary that should be used to open the browser.
*
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#chromium-binary
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#firefox
*/
binaries?: {
/**
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#chromium-binary
*/
chrome?: string;
/**
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#chromium-binary
*/
edge?: string;
/**
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#chromium-binary
*/
opera?: string;
/**
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#firefox
*/
firefox?:
| 'firefox'
| 'beta'
| 'nightly'
| 'deved'
| 'firefoxdeveloperedition'
| string;
};
binaries?: Record<string, string>;
/**
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#firefox-profile
*/
@@ -467,3 +478,8 @@ export interface ExtensionRunnerConfig {
*/
startUrls?: string[];
}
export type WxtViteConfig = Omit<
vite.UserConfig,
'root' | 'configFile' | 'mode'
>;
+2 -6
View File
@@ -20,17 +20,13 @@ export interface InternalConfig {
entrypointsDir: string;
outBaseDir: string;
outDir: string;
storeIds: {
chrome?: string;
firefox?: string;
edge?: string;
};
debug: boolean;
mode: string;
command: 'build' | 'serve';
browser: TargetBrowser;
manifestVersion: TargetManifestVersion;
logger: Logger;
imports: Partial<UnimportOptions>;
imports: false | Partial<UnimportOptions>;
vite: vite.InlineConfig;
manifest: UserManifest;
fsCache: FsCache;
@@ -42,6 +42,7 @@ describe('Entrypoint Utils', () => {
inputPath: '...',
name,
outputDir,
options: {},
};
const actual = getEntrypointOutputFile(entrypoint, ext);
@@ -23,6 +23,7 @@ const unlistedScript: Entrypoint = {
name: 'injected',
inputPath: '/injected.ts',
outputDir: '/.output/injected',
options: {},
};
const popup: Entrypoint = {
type: 'popup',
@@ -36,6 +37,7 @@ const unlistedPage: Entrypoint = {
name: 'onboarding',
inputPath: '/onboarding.html',
outputDir: '/.output/onboarding',
options: {},
};
const options: Entrypoint = {
type: 'options',
@@ -49,24 +51,28 @@ const sandbox1: Entrypoint = {
name: 'sandbox',
inputPath: '/sandbox1.html',
outputDir: '/.output/sandbox1',
options: {},
};
const sandbox2: Entrypoint = {
type: 'sandbox',
name: 'sandbox2',
inputPath: '/sandbox2.html',
outputDir: '/.output/sandbox2',
options: {},
};
const unlistedStyle: Entrypoint = {
type: 'unlisted-style',
name: 'injected',
inputPath: '/injected.scss',
outputDir: '/.output',
options: {},
};
const contentScriptStyle: Entrypoint = {
type: 'content-script-style',
name: 'injected',
inputPath: '/overlay.content.scss',
outputDir: '/.output/content-scripts',
options: {},
};
describe('groupEntrypoints', () => {
+4 -2
View File
@@ -4,13 +4,15 @@ import { mergeConfig } from 'vite';
export function getUnimportOptions(
config: InternalConfig,
): Partial<UnimportOptions> {
): Partial<UnimportOptions | false> {
if (config.imports === false) return false;
const defaultOptions: Partial<UnimportOptions> = {
debugLog: config.logger.debug,
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(
+2 -1
View File
@@ -1,6 +1,7 @@
import fs, { ensureDir } from 'fs-extra';
import { FsCache } from '../types';
import { dirname, resolve } from 'path';
import { writeFileIfDifferent } from './fs';
/**
* A basic file system cache stored at `<srcDir>/.wxt/cache/<key>`. Just caches a string in a
@@ -16,7 +17,7 @@ export function createFsCache(wxtDir: string): FsCache {
async set(key: string, value: string): Promise<void> {
const path = getPath(key);
await ensureDir(dirname(path));
await fs.writeFile(path, value, 'utf-8');
await writeFileIfDifferent(path, value);
},
async get(key: string): Promise<string | undefined> {
const path = getPath(key);
+21
View File
@@ -0,0 +1,21 @@
import fs from 'fs-extra';
/**
* Only write the contents to a file if it results in a change. This prevents unnecessary file
* watchers from being triggered, like WXT's dev server or the TS language server in editors.
*
* @param file The file to write to.
* @param newContents The new text content to write.
*/
export async function writeFileIfDifferent(
file: string,
newContents: string,
): Promise<void> {
const existingContents = await fs
.readFile(file, 'utf-8')
.catch(() => undefined);
if (existingContents !== newContents) {
await fs.writeFile(file, newContents);
}
}
+198 -131
View File
@@ -1,173 +1,115 @@
import { loadConfig } from 'c12';
import {
ConfigEnv,
ExtensionRunnerConfig,
InlineConfig,
InternalConfig,
UserConfig,
UserManifest,
ConfigEnv,
UserManifestFn,
UserManifest,
WxtViteConfig,
ExtensionRunnerConfig,
} from '../types';
import path, { resolve } from 'node:path';
import path from 'node:path';
import * as vite from 'vite';
import { consola } from 'consola';
import * as plugins from '../vite-plugins';
import { createFsCache } from './createFsCache';
import consola, { LogLevels } from 'consola';
import * as plugins from '../vite-plugins';
import { getGlobals } from './globals';
import { loadConfig } from 'c12';
/**
* Given an inline config, discover the config file if necessary, merge the results, resolve any
* relative paths, and apply any defaults.
*
* Inline config always has priority over user config. Cli flags are passed as inline config if set.
* If unset, undefined is passed in, letting this function decide default values.
*/
export async function getInternalConfig(
config: InlineConfig,
inlineConfig: InlineConfig,
command: 'build' | 'serve',
): Promise<InternalConfig> {
// Apply defaults to a base config
const root = config.root ? path.resolve(config.root) : process.cwd();
const mode =
config.mode ?? (command === 'build' ? 'production' : 'development');
const browser = config.browser ?? 'chrome';
const manifestVersion =
config.manifestVersion ?? (browser == 'firefox' ? 2 : 3);
const outBaseDir = path.resolve(root, '.output');
const outDir = path.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
const logger = config.logger ?? consola;
// Load user config
const baseConfig: InternalConfigNoUserDirs = {
root,
outDir,
outBaseDir,
storeIds: config.storeIds ?? {},
browser,
manifestVersion,
mode,
command,
logger,
vite: config.vite ?? {},
imports: config.imports ?? {},
runnerConfig: await loadConfig<ExtensionRunnerConfig>({
name: 'web-ext',
cwd: root,
globalRc: true,
rcFile: '.webextrc',
overrides: config.runner,
}),
};
// Load user config from file
let userConfig: UserConfig = {
mode,
};
if (config.configFile !== false) {
let userConfig: UserConfig = {};
if (inlineConfig.configFile !== false) {
const loaded = await loadConfig<UserConfig>({
name: 'wxt',
cwd: root,
cwd: inlineConfig.root ?? process.cwd(),
rcFile: false,
});
userConfig = loaded.config ?? {};
}
// Merge inline and user configs
const merged = vite.mergeConfig(
baseConfig,
userConfig,
) as InternalConfigNoUserDirs;
// Merge it into the inline config
// Apply user config and create final config
const srcDir = userConfig.srcDir ? resolve(root, userConfig.srcDir) : root;
const entrypointsDir = resolve(
srcDir,
userConfig.entrypointsDir ?? 'entrypoints',
const mergedConfig = mergeInlineConfig(inlineConfig, userConfig);
// Apply defaults to make internal config.
const debug = mergedConfig.debug ?? false;
const logger = mergedConfig.logger ?? consola;
if (debug) logger.level = LogLevels.debug;
const browser = mergedConfig.browser ?? 'chrome';
const manifestVersion =
mergedConfig.manifestVersion ?? (browser == 'firefox' ? 2 : 3);
const mode =
mergedConfig.mode ?? (command === 'build' ? 'production' : 'development');
const env: ConfigEnv = { browser, command, manifestVersion, mode };
const root = path.resolve(
inlineConfig.root ?? userConfig.root ?? process.cwd(),
);
const publicDir = resolve(root, userConfig.publicDir ?? 'public');
const wxtDir = resolve(srcDir, '.wxt');
const typesDir = resolve(wxtDir, 'types');
const wxtDir = path.resolve(root, '.wxt');
const srcDir = path.resolve(root, mergedConfig.srcDir ?? root);
const entrypointsDir = path.resolve(
srcDir,
mergedConfig.entrypointsDir ?? 'entrypoints',
);
const publicDir = path.resolve(srcDir, mergedConfig.publicDir ?? 'public');
const typesDir = path.resolve(wxtDir, 'types');
const outBaseDir = path.resolve(root, '.output');
const outDir = path.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
// Merge manifest sources
const env: ConfigEnv = { mode, browser, manifestVersion, command };
const userManifest = await resolveManifestConfig(env, userConfig.manifest);
const inlineManifest = await resolveManifestConfig(env, config.manifest);
const manifest = vite.mergeConfig(userManifest, inlineManifest);
const runnerConfig = await loadConfig<ExtensionRunnerConfig>({
name: 'web-ext',
cwd: root,
globalRc: true,
rcFile: '.webextrc',
overrides: mergedConfig.runner,
});
const finalConfig: InternalConfig = {
...merged,
srcDir,
browser,
command,
debug,
entrypointsDir,
publicDir,
wxtDir: wxtDir,
typesDir,
fsCache: createFsCache(wxtDir),
manifest,
zip: {
sourcesTemplate: '{{name}}-{{version}}-sources.zip',
artifactTemplate: '{{name}}-{{version}}-{{browser}}.zip',
sourcesRoot: root,
...userConfig.zip,
...config.zip,
ignoredSources: [
'**/node_modules',
// WXT files
'**/web-ext.config.ts',
// Hidden files
'**/.*',
// Tests
'**/__tests__/**',
'**/*.+(test|spec).?(c|m)+(j|t)s?(x)',
// User config
...(userConfig.zip?.ignoredSources ?? []),
...(config.zip?.ignoredSources ?? []),
],
},
imports: mergedConfig.imports ?? {},
logger,
manifest: await resolveManifestConfig(env, mergedConfig.manifest),
manifestVersion,
mode,
outBaseDir,
outDir,
publicDir,
root,
runnerConfig,
srcDir,
typesDir,
vite: {}, // Real value added after this object is initialized.
wxtDir,
zip: resolveInternalZipConfig(root, mergedConfig),
};
// Customize the default vite config
finalConfig.vite.root = root;
finalConfig.vite.configFile = false;
finalConfig.vite.logLevel = 'warn';
finalConfig.vite.build ??= {};
finalConfig.vite.build.outDir = outDir;
finalConfig.vite.build.emptyOutDir = false;
finalConfig.vite.plugins ??= [];
finalConfig.vite.plugins.push(plugins.download(finalConfig));
finalConfig.vite.plugins.push(plugins.devHtmlPrerender(finalConfig));
finalConfig.vite.plugins.push(plugins.unimport(finalConfig));
finalConfig.vite.plugins.push(
plugins.virtualEntrypoin('background', finalConfig),
finalConfig.vite = await resolveInternalViteConfig(
env,
mergedConfig,
finalConfig,
);
finalConfig.vite.plugins.push(
plugins.virtualEntrypoin('content-script', finalConfig),
);
finalConfig.vite.plugins.push(plugins.devServerGlobals(finalConfig));
finalConfig.vite.plugins.push(plugins.tsconfigPaths(finalConfig));
finalConfig.vite.plugins.push(plugins.noopBackground());
finalConfig.vite.define ??= {};
getGlobals(finalConfig).forEach((global) => {
finalConfig.vite.define![global.name] = JSON.stringify(global.value);
});
return finalConfig;
}
/**
* Helper type for defining a base config, since user-configurable directories must be set after
* reading in the user config.
*/
type InternalConfigNoUserDirs = Omit<
InternalConfig,
| 'srcDir'
| 'publicDir'
| 'entrypointsDir'
| 'wxtDir'
| 'typesDir'
| 'fsCache'
| 'manifest'
| 'zip'
>;
async function resolveManifestConfig(
env: ConfigEnv,
manifest: UserManifest | Promise<UserManifest> | UserManifestFn | undefined,
@@ -176,3 +118,128 @@ async function resolveManifestConfig(
? manifest(env)
: manifest ?? {});
}
/**
* Merge the inline config and user config. Inline config is given priority. Defaults are not applied here.
*/
function mergeInlineConfig(
inlineConfig: InlineConfig,
userConfig: UserConfig,
): 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 = vite.mergeConfig(
userConfig.imports ?? {},
inlineConfig.imports ?? {},
);
}
const manifest: UserManifestFn = async (env) => {
const user = await resolveManifestConfig(env, userConfig.manifest);
const inline = await resolveManifestConfig(env, inlineConfig.manifest);
return vite.mergeConfig(user, inline);
};
const viteConfig = async (env: ConfigEnv): Promise<WxtViteConfig> => {
const user = await resolveViteConfig(env, userConfig.vite);
const inline = await resolveViteConfig(env, inlineConfig.vite);
return vite.mergeConfig(user, inline);
};
const runner: InlineConfig['runner'] = vite.mergeConfig(
userConfig.runner ?? {},
inlineConfig.runner ?? {},
);
const zip: InlineConfig['zip'] = vite.mergeConfig(
userConfig.zip ?? {},
inlineConfig.zip ?? {},
);
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,
imports,
logger: inlineConfig.logger ?? userConfig.logger,
manifest,
mode: inlineConfig.mode ?? userConfig.mode,
publicDir: inlineConfig.publicDir ?? userConfig.publicDir,
runner,
srcDir: inlineConfig.srcDir ?? userConfig.srcDir,
vite: viteConfig,
zip,
};
}
async function resolveViteConfig(
env: ConfigEnv,
vite: InlineConfig['vite'],
): Promise<WxtViteConfig> {
return await (typeof vite === 'function' ? vite(env) : vite ?? {});
}
function resolveInternalZipConfig(
root: string,
mergedConfig: InlineConfig,
): InternalConfig['zip'] {
return {
sourcesTemplate: '{{name}}-{{version}}-sources.zip',
artifactTemplate: '{{name}}-{{version}}-{{browser}}.zip',
sourcesRoot: root,
...mergedConfig.zip,
ignoredSources: [
'**/node_modules',
// WXT files
'**/web-ext.config.ts',
// Hidden files
'**/.*',
// Tests
'**/__tests__/**',
'**/*.+(test|spec).?(c|m)+(j|t)s?(x)',
// From user
...(mergedConfig.zip?.ignoredSources ?? []),
],
};
}
async function resolveInternalViteConfig(
env: ConfigEnv,
mergedConfig: InlineConfig,
finalConfig: InternalConfig,
) {
const internalVite: vite.InlineConfig = await resolveViteConfig(
env,
mergedConfig.vite,
);
internalVite.root = finalConfig.root;
internalVite.configFile = false;
internalVite.logLevel = 'warn';
internalVite.build ??= {};
internalVite.build.outDir = finalConfig.outDir;
internalVite.build.emptyOutDir = false;
internalVite.plugins ??= [];
internalVite.plugins.push(plugins.download(finalConfig));
internalVite.plugins.push(plugins.devHtmlPrerender(finalConfig));
internalVite.plugins.push(plugins.unimport(finalConfig));
internalVite.plugins.push(
plugins.virtualEntrypoin('background', finalConfig),
);
internalVite.plugins.push(
plugins.virtualEntrypoin('content-script', finalConfig),
);
internalVite.plugins.push(plugins.devServerGlobals(finalConfig));
internalVite.plugins.push(plugins.tsconfigPaths(finalConfig));
internalVite.plugins.push(plugins.noopBackground());
internalVite.define ??= {};
for (const global of getGlobals(finalConfig)) {
internalVite.define[global.name] = JSON.stringify(global.value);
}
return internalVite;
}
+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,
}));
}
@@ -5,7 +5,7 @@ import fs from 'fs-extra';
import { resolve } from 'path';
import transform from 'jiti/dist/babel';
import { getUnimportOptions } from './auto-imports';
import { removeImportStatements } from './strings';
import { removeProjectImportStatements } from './strings';
import { normalizePath } from './paths';
/**
@@ -23,7 +23,7 @@ import { normalizePath } from './paths';
* Downside is that code cannot be executed outside of the main fucntion for the entrypoint,
* otherwise you will see "xxx is not defined" errors for any imports used outside of main function.
*/
export async function importTsFile<T>(
export async function importEntrypointFile<T>(
path: string,
config: InternalConfig,
): Promise<T> {
@@ -39,7 +39,7 @@ export async function importTsFile<T>(
await unimport.init();
const text = await fs.readFile(path, 'utf-8');
const textNoImports = removeImportStatements(text);
const textNoImports = removeProjectImportStatements(text);
const { code } = await unimport.injectImports(textNoImports);
config.logger.debug(
['Text:', text, 'No imports:', textNoImports, 'Code:', code].join('\n'),
+43 -2
View File
@@ -17,6 +17,8 @@ import {
mapWxtOptionsToContentScript,
} from './content-scripts';
import { getPackageJson } from './package';
import { normalizePath } from './paths';
import { writeFileIfDifferent } from './fs';
/**
* Writes the manifest to the output directory and the build output.
@@ -32,7 +34,7 @@ export async function writeManifest(
: JSON.stringify(manifest, null, 2);
await fs.ensureDir(config.outDir);
await fs.writeFile(resolve(config.outDir, 'manifest.json'), str, 'utf-8');
await writeFileIfDifferent(resolve(config.outDir, 'manifest.json'), str);
output.publicAssets.unshift({
type: 'asset',
@@ -65,6 +67,7 @@ export async function generateMainfest(
? pkg?.version
: undefined,
short_name: pkg?.shortName,
icons: discoverIcons(buildOutput),
},
config.manifest,
);
@@ -300,7 +303,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 +316,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,
+11
View File
@@ -15,3 +15,14 @@ export function removeImportStatements(text: string): string {
'',
);
}
/**
* Removes imports, ensuring that some of WXT's client imports are present, so that entrypoints can be parsed if auto-imports are disabled.
*/
export function removeProjectImportStatements(text: string): string {
const noImports = removeImportStatements(text);
return `import { defineContentScript, defineBackground } from 'wxt/client';
${noImports}`;
}
+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
@@ -1,7 +1,7 @@
import { createUnimport } from 'unimport';
import { InternalConfig } from '../types';
import { getUnimportOptions } from '../utils/auto-imports';
import { Plugin } from 'vite';
import * as vite from 'vite';
import { extname } from 'path';
const ENABLED_EXTENSIONS: Record<string, boolean | undefined> = {
@@ -16,8 +16,10 @@ const ENABLED_EXTENSIONS: Record<string, boolean | undefined> = {
/**
* Inject any global imports defined by unimport
*/
export function unimport(config: InternalConfig): Plugin {
export function unimport(config: InternalConfig): vite.PluginOption {
const options = getUnimportOptions(config);
if (options === false) return [];
const unimport = createUnimport(options);
return {
+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;
}

Some files were not shown because too many files have changed in this diff Show More