Compare commits

...

92 Commits

Author SHA1 Message Date
github-actions[bot] 1a3f4f64e5 chore(release): wxt v0.20.27
📼 VHS / Create VHS (push) Waiting to run
2026-06-23 18:08:15 +00:00
Patryk Kuniczak 492cbb4644 fix: Rename ManifestPermissions to ManifestPermission of types.ts in wxt package (#2430) 2026-06-23 12:57:50 -05:00
aklinker1 c8e93b15db fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2026-06-21 01:16:16 +00:00
RenovZ e3f4fe3e13 docs: added anylang translator to showcase (#2426) 2026-06-19 20:01:03 +00:00
Nicholas Yoannou 8cb973cf3c docs: Added Hayami to showcase (#2422) 2026-06-18 17:39:17 +00:00
Avi 22d1a72752 docs: added 4 extensions to the homepage (#2419) 2026-06-16 19:32:40 +00:00
Peter Wong 8f90331229 docs: Added NiceTab firefoxslug to extension showcase (#2417)
Co-authored-by: wangpeng618 <wangpeng618@jd.com>
2026-06-16 19:01:39 +00:00
ChamikaUluwatta 9a638449d5 fix: correct broken link at react router in createhashrouter (#2411) 2026-06-09 18:00:21 +00:00
Muhammed Mustafa AKŞAM 9ce9b714f6 docs: Added "LinkedIn Games Solver" to showcase (#2410) 2026-06-09 14:35:31 +00:00
aklinker1 22b7aba035 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2026-06-06 01:04:14 +00:00
Slovakian 4cbcf282f7 docs: Added "WageSlav3" to showcase (#2405)
Co-authored-by: Patryk Kuniczak <p.kuniczak@gmail.com>
2026-06-05 20:56:03 +00:00
Oliver Benns d8b1ddfb59 docs: Added Property Track to showcase (#2404) 2026-06-05 09:08:58 +00:00
David Nguyen 7905cb379b chore: Add Google Cloud Skills Boost - Helper extension to showcase (#2401) 2026-06-02 14:35:05 +00:00
Esteban Vera 1b2463d52e "docs: Added "Link Later" to showcase" (#2392) 2026-05-22 15:09:42 +00:00
Saksham Kushwaha b567f9957a Add firefoxSlug for Monochromate extension (#2389) 2026-05-20 14:17:28 +00:00
Sean Kenneth Doherty c2fb8c786d docs: self-host Netlify footer badge (#2386) 2026-05-18 06:08:24 +00:00
Muhammad Umer Farooq 4320d8e1fa docs: clarify SPA navigation setup with locationWatcher.run() (#2380)
Co-authored-by: Patryk Kuniczak <p.kuniczak@gmail.com>
2026-05-16 17:37:56 -07:00
Muzammil 344d14dafc docs: Added "Sound Booster & Equalizer" to showcase (#2381) 2026-05-15 16:21:59 +02:00
Aaron 3cb894c4e5 chore: Add benchmarks for i18n vs vanilla usage (#2372) 2026-05-14 06:41:42 -07:00
Aniket lodh dbbe14ddd6 docs: add warning about rem unit inheritance in Shadow Root UI (#2330)
Co-authored-by: Patryk Kuniczak <p.kuniczak@gmail.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-12 18:19:51 +00:00
Aaron 9ee07f431a chore: Fix lockfile 2026-05-12 07:34:44 -05:00
dependabot[bot] d517e9a0b9 chore(deps): bump tinyglobby from 0.2.15 to 0.2.16 (#2366)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 07:27:17 -05:00
dependabot[bot] d9adbb6f09 chore(deps): bump pnpm/action-setup from 6.0.6 to 6.0.7 (#2362)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 07:26:48 -05:00
dependabot[bot] 6b77277cea chore(deps): bump filesize from 11.0.15 to 11.0.17 (#2363)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 07:26:30 -05:00
dependabot[bot] bad24392fc chore(deps): bump c12 from 3.3.3 to 3.3.4 (#2364)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-12 07:26:13 -05:00
George Forse f24e62129b docs: Add Telsia extension to Showcase (#2368) 2026-05-12 07:13:58 -05:00
Aaron f38cb855ea ci: Don't error PR when author assignment fails 2026-05-12 07:12:44 -05:00
dependabot[bot] 891a0d50a6 chore(deps-dev): bump @vitest/coverage-v8 from 4.1.5 to 4.1.6 (#2367)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 07:09:42 -05:00
Shubhrajit Sadhukhan cfb384e618 feat: Add warning for missing Firefox extension ID in manifest (#2293)
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Patryk Kuniczak <p.kuniczak@gmail.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-12 12:08:38 +00:00
github-actions[bot] 6f14aa1c71 chore(release): wxt v0.20.26
📼 VHS / Create VHS (push) Waiting to run
2026-05-11 12:35:14 +00:00
Jonathan Viney f771e6a122 feat: Add content script noScriptStartedPostMessage option (#2265)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-11 12:25:14 +00:00
Aaron a0a23947b6 chore(deps): Upgrade vitest and related deps (#2358) 2026-05-10 12:39:07 -05:00
rxliuli 7b6f1dc88b fix(manifest): exclude open_in_tab from options_ui for Safari (#2311)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 17:32:54 +00:00
Aaron 16f9e1705f chore: Update codeowners
I  was not an owner of the docs directory, you have to add yourself to every one you care about, the last matching rule wins, it doesn't combine them
2026-05-10 12:19:36 -05:00
CrawlerCode 40e28b702d docs: Add "Redmine Time Tracking" to extension showcase (#2312)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 12:16:27 -05:00
Thomas Howlett 75f0b843f8 docs: update Safari publishing instructions to match Apple docs (#2314)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 12:12:46 -05:00
Aaron 8793385325 chore: Use catalog: for dev dependencies (#2357) 2026-05-10 11:56:11 -05:00
Thomas Howlett 4f86143853 fix: avoid errors when files are removed during build (#2343)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 16:19:52 +00:00
dependabot[bot] 498f74b21b chore(deps-dev): bump oxlint from 1.59.0 to 1.63.0 (#2356)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 11:16:05 -05:00
dependabot[bot] 300798eb11 chore(deps-dev): bump vitepress-plugin-llms from 1.12.0 to 1.12.2 (#2352)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 11:06:04 -05:00
Aaron d482fe6832 ci: Group dependabot PRs 2026-05-10 11:05:46 -05:00
dependabot[bot] 955bb20914 chore(deps-dev): bump unocss from 66.6.0 to 66.6.8 (#2353)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 10:59:12 -05:00
dependabot[bot] 22339d261b chore(deps-dev): bump nano-staged from 0.8.0 to 1.0.2 (#2355)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 10:58:57 -05:00
dependabot[bot] 5d89a40c6d chore(deps-dev): bump vue from 3.5.32 to 3.5.34 (#2350)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 10:54:50 -05:00
Aaron c1fe602c8b ci: Tell dependabot to use bun 2026-05-10 10:49:21 -05:00
dependabot[bot] bea5e0da1e chore(deps): bump actions/github-script from 8.0.0 to 9.0.0 (#2321)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 10:46:47 -05:00
Brian Zhou 7907e56a31 fix: bump publish-browser-extension to v4.0.5, to resolve Chrome Web Store submission issue (#2331) 2026-05-10 10:37:17 -05:00
Aaron 4e43e6a315 chore: Upgrade pkg.pr.new and remove patch (#2349) 2026-05-10 10:36:08 -05:00
dependabot[bot] 78ef08390e chore(deps): bump docker/login-action from 4.0.0 to 4.1.0 (#2323)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 10:35:37 -05:00
dependabot[bot] b5235ed4f0 chore(deps-dev): bump typescript from 5.9.3 to 6.0.3 (#2325)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 10:33:47 -05:00
Aaron 868a567ebd ci: Run dependabot daily 2026-05-10 10:22:58 -05:00
dependabot[bot] 0797041643 chore(deps): bump actions/setup-node from 5.0.0 to 6.4.0 (#2322)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 10:17:51 -05:00
dependabot[bot] 60b22be29d chore(deps): bump pnpm/action-setup from 5.0.0 to 6.0.6 (#2324)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 10:09:06 -05:00
Aaron 1a817e1367 ci: Fix failing tests by pinning PNPM version 2026-05-10 09:59:07 -05:00
Patryk Kuniczak 2be3044f6a chore: Add me to CODEOWNERS for docs directory (#2346) 2026-05-10 09:55:31 -05:00
Aaron 59f6f4b926 ci: Update self-hosted docs setup 2026-05-10 09:54:09 -05:00
Thomas Howlett 3d2d36954e chore: add missing tsdown dependencies (#2316) 2026-05-10 09:02:36 -05:00
Nadeem Siddique e44cb3950f Added Headstart to showcase (#2345) 2026-05-10 09:00:21 -05:00
Thomas Howlett 06682a6edc chore: remove ts-expect-error that are no longer needed (#2344) 2026-05-10 08:56:57 -05:00
Soaib Haque e6e513a34b docs: Added "QIE Wallet" to showcase (#2341)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 08:54:56 -05:00
Arthur 13dd3742a8 docs: added modrinth extras and pi-hole in one to showcase (#2337)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-05-10 08:54:00 -05:00
Luis Ortiz c27278ecfb docs: Add Mimik extension to the list of extensions (#2319) 2026-05-10 08:52:56 -05:00
Patryk Kuniczak 2f68c6e344 docs: fix horizontal scrollbar displaying on landing page (#2329) 2026-05-10 08:44:39 -05:00
aklinker1 a7976d2e63 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2026-05-08 00:57:25 +00:00
laplace-anon c15167ca86 fix: Modify command to support variadic positional args (#2317) 2026-04-30 22:27:12 +00:00
Aaron e75e534882 chore: Keep lockfile in sync when generating @wxt-dev/browser (#2310) 2026-04-26 22:26:24 -05:00
Aaron b21ec34a65 chore: Upgrade bun to 1.3.13 (#2309) 2026-04-26 22:11:04 -05:00
Aaron e45b1fb8df chore: Move createFileReloader into it's own file (#2307) 2026-04-26 21:46:00 -05:00
Aaron 0b3c3ab39d ci: Remove unnecessary --frozen-lockfile 2026-04-26 21:34:10 -05:00
Aaron 8b530bc1c9 fix: Use manifestVersion from CLI during manifest generation (#2306) 2026-04-26 20:58:58 -05:00
Aaron a2212f7a89 chore: Update lockfile 2026-04-26 20:53:11 -05:00
kairi e613b54d94 feat: add default_state option to popup actions (#2010)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-04-26 17:01:59 -05:00
Patryk Kuniczak d9c140114a docs: Remove unused code (#2275) 2026-04-26 16:02:22 -05:00
Aaron 9b59f38c0d chore: More JSDoc fixes 2026-04-26 15:21:19 -05:00
Aaron 2a8ec0d786 docs: Fix JSDoc example formatting 2026-04-26 15:18:26 -05:00
Patryk Kuniczak e29fad16ea docs: Add types for components (#2273) 2026-04-26 14:50:52 -05:00
Patryk Kuniczak 50d223a30a ci: use --frozen-lockfile in action.yml for bun (#2300) 2026-04-26 14:47:55 -05:00
Thomas Howlett 1dbdf4ea9e docs: correct entrypoints icon example code (#2302) 2026-04-25 19:14:46 +00:00
aklinker1 09b5a19577 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2026-04-19 00:52:20 +00:00
Aaron b5ea72e214 fix: Use obug for debug logs in runner (#2281) 2026-04-18 09:35:32 -05:00
Aaron 57e3748d79 docs(wxt-modules): Add logging examples/best practices 2026-04-18 09:33:48 -05:00
github-actions[bot] 155626fbed chore(release): wxt v0.20.25
📼 VHS / Create VHS (push) Waiting to run
2026-04-18 14:24:37 +00:00
Aaron e802aa211f docs: Update permissions section with per-browser warning (#2284) 2026-04-18 09:08:09 -05:00
Aaron 54fe8c6687 fix: Port change in dev server on reload (#2283) 2026-04-18 08:42:04 -05:00
aklinker1 5da6e4ff75 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2026-04-18 00:46:17 +00:00
Aaron 4090bb100e chore: Simplify tests (#2279) 2026-04-17 12:01:15 -05:00
Aaron 54ed99d91d chore: Update browser fallbacks (#2280) 2026-04-17 12:00:19 -05:00
Aaron 3db3eac62a chore: Increase PNPM test timeout 2026-04-17 11:55:51 -05:00
Patryk Kuniczak 0315882fa4 chore: Add new types for i18n (#2121) 2026-04-17 14:38:22 +00:00
github-actions[bot] 9757e2634f chore(release): wxt v0.20.24
📼 VHS / Create VHS (push) Waiting to run
2026-04-17 14:32:45 +00:00
John Wong 42b55f66fb fix: Correct actionKey for Firefox in MV3 (#2274) 2026-04-17 14:12:37 +00:00
aklinker1 e64058bc94 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2026-04-17 00:50:56 +00:00
100 changed files with 3269 additions and 2627 deletions
+3
View File
@@ -7,3 +7,6 @@
# Creator of specific wxt modules
/packages/auto-icons/ @Timeraa
/packages/unocss/ @Timeraa
# Other Directories
/docs @aklinker1 @PatrykKuniczak
+12 -7
View File
@@ -1,15 +1,20 @@
# 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
- package-ecosystem: bun
directory: /
schedule:
interval: 'monthly'
interval: 'daily'
groups:
production-updates:
applies-to: version-updates
dependency-type: production
update-types: [minor, patch]
dev-updates:
applies-to: version-updates
dependency-type: development
update-types: [minor, patch]
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'monthly'
interval: 'daily'
+14 -10
View File
@@ -13,7 +13,7 @@ jobs:
steps:
- name: Gather Info
id: check
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const { data: pr } = await github.rest.pulls.get({
@@ -59,20 +59,24 @@ jobs:
- name: Sync Author
if: steps.check.outputs.has_assignees == 'false'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
assignees: ['${{ steps.check.outputs.author }}']
});
try {
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
assignees: ['${{ steps.check.outputs.author }}']
});
console.log('Assigned PR author: ${{ steps.check.outputs.author }}');
console.log('Assigned PR author: ${{ steps.check.outputs.author }}');
} catch (err) {
console.warn("Error assigning author:", err);
}
- name: Sync Labels
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const newLabels = ${{ steps.check.outputs.labels }};
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
run: bun run buildc all
- name: Publish
run: bun pkg-pr-new publish --compact --bun './packages/*'
run: bunx pkg-pr-new publish --compact --bun './packages/*'
- name: DEBUG
if: always()
+1 -1
View File
@@ -29,7 +29,7 @@ jobs:
github.event.pull_request.user.login != 'PatrykKuniczak' &&
github.event.pull_request.user.login != 'dependabot[bot]' &&
github.event.pull_request.user.login != 'copilot-swe-agent[bot]'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
comment: Thanks for helping make WXT better!
with:
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
uses: ./.github/actions/setup
- name: Login to Docker Registry
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: https://${{ secrets.DOCKER_REGISTRY_HOSTNAME }}
username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
@@ -44,4 +44,4 @@ jobs:
run: docker push ${{ secrets.DOCKER_REGISTRY_HOSTNAME }}/wxt/docs:${{ github.event.inputs.tag || 'latest' }}
- name: Deploy
run: curl -X POST -i ${{ secrets.UPDATE_DOCS_WEBHOOK }}
run: 'curl -X POST -i -H "X-API-Key: ${{ secrets.DOCS_WEBHOOK_API_KEY }}" https://${{ secrets.DOCS_WEBHOOK_DOMAIN }}/api/webhook/stacks/${{ secrets.DOCS_WEBHOOK_STACK_ID }}'
+2 -2
View File
@@ -74,9 +74,9 @@ jobs:
uses: ./.github/actions/setup
- name: Setup PNPM
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
uses: pnpm/action-setup@739bfe42ca9233c5e6aca07c1a25a9d34aca49b0 # v6.0.7
with:
version: latest
version: 10.33.0
- name: Run Tests
if: ${{ ! matrix.coverage }}
+2 -2
View File
@@ -21,10 +21,10 @@ jobs:
with:
ref: ${{ github.ref_name }}
- name: Setup PNPM
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
uses: pnpm/action-setup@739bfe42ca9233c5e6aca07c1a25a9d34aca49b0 # v6.0.7
- name: Setup NodeJS
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
cache: pnpm
+1 -1
View File
@@ -1,2 +1,2 @@
bun 1.3.12
bun 1.3.13
nodejs 24.14.1
Generated
+306 -325
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -11,6 +11,7 @@ ignorePaths:
- patches/**
- '**/*.snap'
- docs/assets/extension-showcase.yml
- packages/*/stats.html
words:
- Aabid
- aabidk
@@ -21,6 +22,7 @@ words:
- buildc
- bunx
- cachable
- cacjs
- charmbracelet
- chromedriver
- Cira
@@ -78,6 +80,7 @@ words:
- Noto
- ntvs
- nypm
- obug
- ohash
- oklab
- oxlint
+2 -1
View File
@@ -3,8 +3,9 @@ import { computed } from 'vue';
// @ts-expect-error: Vitepress data-loader magic, this import is correct
import { data } from '../loaders/blog.data';
import BlogPostPreview from './BlogPostPreview.vue';
import { Post } from '../utils/types';
const posts = computed(() =>
const posts = computed<Post[]>(() =>
data
.map((post) => ({
...post,
+6 -5
View File
@@ -1,21 +1,22 @@
<script lang="ts" setup>
import useBlogDate from '../composables/useBlogDate';
import { useData } from 'vitepress';
import { Content, useData } from 'vitepress';
import { PostFrontmatter } from '../utils/types';
const { frontmatter } = useData();
const { frontmatter } = useData<PostFrontmatter>();
const date = useBlogDate(() => frontmatter.value.date);
</script>
<template>
<div class="vp-doc">
<main class="container-content">
<h1 v-html="$frontmatter.title" />
<h1 v-html="frontmatter.title" />
<p class="meta-row">
<a
class="author"
v-for="author of $frontmatter.authors"
v-for="author of frontmatter.authors"
:key="author.github"
:href="`https://github.com/${author.github}`"
class="author"
>
<img :src="`https://github.com/${author.github}.png?size=96`" />
<span>{{ author.name }}</span>
@@ -1,5 +1,5 @@
<script lang="ts" setup>
const props = defineProps<{
defineProps<{
patterns: Array<[intput: string, output: string]>;
}>();
</script>
+3 -3
View File
@@ -1,10 +1,10 @@
<script lang="ts" setup>
import { ref, onMounted, computed, toRaw, Ref } from 'vue';
import { computed, onMounted, ref, Ref, toRaw } from 'vue';
import ExampleSearchFilterByItem from './ExampleSearchFilterByItem.vue';
import ExampleSearchResult from './ExampleSearchResult.vue';
import { ExamplesMetadata, KeySelectedObject } from '../utils/types';
const props = defineProps<{
defineProps<{
tag?: string;
}>();
@@ -48,7 +48,7 @@ function doesExampleMatchSelected(
const filteredExamples = computed(() => {
const text = searchText.value.toLowerCase();
return exampleMetadata.value.examples.filter((example) => {
return exampleMetadata.value?.examples.filter((example) => {
const matchesText = example.searchText.toLowerCase().includes(text);
const matchesApis = doesExampleMatchSelected(example.apis, requiredApis);
const matchesPermissions = doesExampleMatchSelected(
@@ -2,7 +2,7 @@
import { computed, toRaw } from 'vue';
import { KeySelectedObject } from '../utils/types';
const props = defineProps<{
defineProps<{
label: string;
items?: string[];
}>();
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { Example } from '../utils/types';
const props = defineProps<{
defineProps<{
example: Example;
}>();
</script>
+5 -2
View File
@@ -1,4 +1,5 @@
import { Feed } from 'feed';
// @ts-expect-error; It isn't TypeScript lib
import footnote from 'markdown-it-footnote';
import fs, { readFile } from 'node:fs/promises';
import { join } from 'node:path';
@@ -73,8 +74,11 @@ export default defineConfig({
description,
vite: {
clearScreen: false,
//TODO: REMOVE THIS @TS-EXPECT-ERROR AFTER BUMP VITEPRESS TO V2.0
plugins: [
// @ts-expect-error: Vite version mismatch between this project and the plugin
llmstxt(),
// @ts-expect-error: Vite version mismatch between this project and the plugin
groupIconVitePlugin({
customIcon: {
'wxt.config.ts': localIconLoader(
@@ -159,8 +163,7 @@ export default defineConfig({
footer: {
message: [
'<a class="light-netlify" href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-bg.svg" alt="Deploys by Netlify" style="display: inline;" /></a>',
'<a class="dark-netlify" href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" style="display: inline;" /></a>',
'<span class="netlify-badge"><a class="light-netlify" href="https://www.netlify.com"><img src="/netlify-badge-light.svg" alt="Deploys by Netlify" /></a><a class="dark-netlify" href="https://www.netlify.com"><img src="/netlify-badge-dark.svg" alt="Deploys by Netlify" /></a></span>',
'Released under the <a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE">MIT License</a>.',
].join('<br/>'),
copyright:
+8 -2
View File
@@ -1,3 +1,9 @@
import { createContentLoader } from 'vitepress';
import { type ContentData, createContentLoader } from 'vitepress';
import { PostFrontmatter } from '../utils/types';
export default createContentLoader('blog/*.md');
declare const data: Array<ContentData & { frontmatter: PostFrontmatter }>;
export { data };
export default createContentLoader<
Array<ContentData & { frontmatter: PostFrontmatter }>
>('blog/*.md');
-5
View File
@@ -56,8 +56,3 @@ async function getPublishExtensionHelp(command: string): Promise<string> {
);
return res.replace(/\$ publish-extension/g, '$ wxt submit');
}
export interface Command {
name: string;
docs: string;
}
+14 -4
View File
@@ -54,8 +54,18 @@ body {
background-color: var(--vp-c-default-2);
}
.netlify-badge {
display: inline-flex;
line-height: 0;
vertical-align: middle;
}
.netlify-badge img {
display: block;
}
.light-netlify {
display: inline;
display: inline-flex;
}
.dark .light-netlify {
display: none;
@@ -64,7 +74,7 @@ body {
display: none;
}
.dark .dark-netlify {
display: inline;
display: inline-flex;
}
/* Add Aaron's favorite pic from Artemis II to the hero section because it's cool. */
@@ -73,7 +83,7 @@ body {
background-image: url('/assets/art002e009298.jpg');
opacity: 50%;
position: absolute;
width: 100vw;
width: 100%;
height: 110vh;
background-size: cover;
background-position: 90%;
@@ -82,7 +92,7 @@ body {
.dark .VPHome::after {
content: '';
position: absolute;
width: 100vw;
width: 100%;
height: 10vh;
top: 100vh;
background: linear-gradient(to bottom, transparent, var(--vp-c-bg));
+2 -1
View File
@@ -6,10 +6,11 @@ import ExampleSearch from '../components/ExampleSearch.vue';
import BlogLayout from '../components/BlogLayout.vue';
import './custom.css';
import 'virtual:group-icons.css';
import type { EnhanceAppContext } from 'vitepress/client';
export default {
extends: DefaultTheme,
enhanceApp(ctx) {
enhanceApp(ctx: EnhanceAppContext) {
ctx.app
.component('Icon', Icon)
.component('EntrypointPatterns', EntrypointPatterns)
+1 -1
View File
@@ -1,4 +1,4 @@
import { HeadConfig } from 'vitepress/types/shared';
import type { HeadConfig } from 'vitepress';
export function meta(
property: string,
+1 -11
View File
@@ -1,8 +1,6 @@
import { DefaultTheme } from 'vitepress';
type SidebarItem = DefaultTheme.SidebarItem;
type NavItem = DefaultTheme.NavItem;
type NavItemWithLink = DefaultTheme.NavItemWithLink;
type NavItemWithChildren = DefaultTheme.NavItemWithChildren;
type NavItemChildren = DefaultTheme.NavItemChildren;
@@ -19,7 +17,7 @@ export function navItem(text: string, arg2?: unknown): any {
}
export function menuRoot(items: SidebarItem[]) {
return items.map((item, index) => {
return items.map((item) => {
// item.collapsed = false; // uncomment to expand all level-0 items
return item;
});
@@ -60,12 +58,6 @@ export function menuGroup(
throw Error('Unknown overload');
}
export function menuItems(items: SidebarItem[]) {
return {
items,
};
}
export function menuItem(
text: string,
link: string,
@@ -97,9 +89,7 @@ export function prepareTypedocSidebar(items: SidebarItem[]) {
}
};
// process
prepareItems(filtered);
// return
return filtered;
}
+14
View File
@@ -1,3 +1,5 @@
import { ContentData } from 'vitepress';
export interface Example {
name: string;
description?: string;
@@ -16,3 +18,15 @@ export type ExamplesMetadata = {
};
export type KeySelectedObject = Record<string, boolean | undefined>;
export interface PostFrontmatter {
title: string;
description?: string;
date: Date;
authors: { name: string; github: string }[];
}
export interface Post
extends Omit<ContentData, 'frontmatter'>, Omit<PostFrontmatter, 'date'> {
date: Date;
}
+73
View File
@@ -11,6 +11,10 @@
#
# You may include one or more of these fields for your extension.
- # Anylang Translator
chromeId: injdenmpjncknciccmjjmjlgbmnkince
firefoxSlug: anylang-translator
- # GitHub: Better Line Counts
chromeId: ocfdgncpifmegplaglcnglhioflaimkd
firefoxSlug: github-better-line-counts
@@ -105,6 +109,7 @@
- # NiceTab - https://github.com/web-dahuyou/NiceTab
chromeId: fonflmjnjbkigocpoommgmhljdpljain
firefoxSlug: nice-tab-manager
- # Draftly for LinkedIn
chromeId: fcffekbnfcfdemeekijbbmgmkognnmkd
@@ -138,6 +143,22 @@
chromeId: fcphghnknhkimeagdglkljinmpbagone
firefoxSlug: youtube-auto-hd-fps
- # YouTube Auto Feed
chromeId: jcdebdlnakhdinkindpogcehnhggbfad
firefoxSlug: youtube-auto-feed
- # YouTube Like-Dislike Shortcut
chromeId: fdkpkpelkkdkjhpacficichkfifijipc
firefoxSlug: youtube-like-dislike-shortcut
- # Twitch Channel Points Bonus Collector
chromeId: kbbdnbaghpcjpdhbjbccadodjejlkkgg
firefoxSlug: twitch-cp-bonus-collector
- # Skillshare Player Control
chromeId: agbhgcomfpcfboebbfmefbicfkpnlfeg
firefoxSlug: skillshare-player-control
- # MultiViewer Companion
chromeId: lpomjgbicdemjkgmbnkjncgdebogkhlb
firefoxSlug: multiviewer-companion
@@ -217,6 +238,7 @@
- # Monochromate
chromeId: hafcajcllbjnoolpfngclfmmgpikdhlm
firefoxSlug: monochromate
- # AliasVault - Open-Source Password & (Email) Alias Manager
chromeId: bmoggiinmnodjphdjnmpcnlleamkfedj
@@ -436,3 +458,54 @@
- # TF2 Trader - TF2 & Steam Trading Extension - https://github.com/offish/tf2-trader
chromeId: gmicpekfpbikhibodgokfpghadkclhoe
- # Mimik
chromeId: jmfohdaflahliammccpiadmkcibohgha
firefoxSlug: mimik
- # Modrinth Extras - https://github.com/creeperkatze/modrinth-extras
chromeId: ajmkilipadfpaefpcjfgnkejalmhdlcj
firefoxSlug: modrinth-extras
- # Pi-hole In One - https://github.com/creeperkatze/pihole-in-one
chromeId: gaaobidjebianpcngcfpkniaocibidhe
firefoxSlug: pihole-in-one
- # QIE Wallet Non-Custodial Multi-Chain Crypto Wallet
chromeId: oljchdcgmibnjbbopolafbjncfhdacjb
- # Headstart Supercharge your browsing efficiency https://useheadstart.app/
chromeId: fhhldbfombjdnjnpeojjohloohmbanda
- # Redmine Time Tracking
chromeId: ldcanhhkffokndenejhafhlkapflgcjg
firefoxSlug: redmine-time-tracking
- # Telsia: Eye Strain Reduction
chromeId: iakkmikmbknfcffhalijnhijblikoldc
firefoxSlug: telsia-eye-strain-reduction
- # Sound Booster & Equalizer
chromeId: podnfipggfpfcpglhjebcefinglohbhn
- # Link Later
chromeId: gpehbbegbcpjmipphokcmfhkchhcpfam
- # Google Cloud Skills Boost - Helper
chromeId: lmbhjioadhcoebhgapaidogodllonbgg
firefoxSlug: cloud-skills-boost-helper
- # Property Track - https://www.propertytrack.co
chromeId: ahknifalnhmbanoclebllcolcdomfdop
firefoxSlug: propertytrack
- # WageSlav3
chromeId: anhkcmapjhiiofpcgglfljphdgppilof
- # LinkedIn Games Solver
chromeId: jnhgapnkejaijibcdhcldhdfikjmdaph
firefoxSlug: linkedin-games-solver-addon
- # Hayami: Anime comments & discussions
chromeId: nhkggpiaeaeeeimohfpchnjobbamfcbg
firefoxSlug: hayami
+17
View File
@@ -200,6 +200,23 @@ export default defineConfig({
});
```
:::warning
Different browsers support different permissions. You are responsible for passing only the permissions required for each browser:
```ts
export default defineConfig({
manifest: ({ browser }) => ({
permissions:
browser === 'chrome'
? ['storage', 'favicon', 'declarativeNetRequest']
: ['storage', 'webRequest'],
}),
});
```
:::
## Host Permissions
> [Chrome docs](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#host-permissions)
+10
View File
@@ -449,6 +449,15 @@ Full examples:
- [react-content-script-ui](https://github.com/wxt-dev/examples/tree/main/examples/react-content-script-ui)
- [tailwindcss](https://github.com/wxt-dev/examples/tree/main/examples/tailwindcss)
:::warning `rem` Units Are Not Fully Isolated
WXT resets most inherited styles via `all: initial`. This doesn't reset the `<html>` element's font size, which determines the relative size of `rem` units.
If your CSS framework uses `rem` units, like Tailwind CSS, you may notice your UI's scale changing on different websites.
See the [FAQ](/guide/resources/faq#my-content-script-ui-looks-different-on-certain-websites) for a fix.
:::
### IFrame
If you don't need to run your UI in the same frame as the content script, you can use an IFrame to host your UI instead. Since an IFrame just hosts an HTML page, **_HMR is supported_**.
@@ -725,6 +734,7 @@ const watchPattern = new MatchPattern('*://*.youtube.com/watch*');
export default defineContentScript({
matches: ['*://*.youtube.com/*'],
main(ctx) {
ctx.locationWatcher.run();
ctx.addEventListener(window, 'wxt:locationchange', ({ newUrl }) => {
if (watchPattern.includes(newUrl)) mainWatch(ctx);
});
+4 -4
View File
@@ -463,8 +463,8 @@ When you define a Newtab entrypoint, WXT will automatically update the manifest
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
'16': '/icon-16.png',
'24': '/icon-24.png',
...
}"
/>
@@ -554,8 +554,8 @@ Firefox does not support sandboxed pages.
<meta
name="manifest.default_icon"
content="{
16: '/icon-16.png',
24: '/icon-24.png',
'16': '/icon-16.png',
'24': '/icon-24.png',
...
}"
/>
+1 -1
View File
@@ -97,7 +97,7 @@ Instead, you need to configure the router to run in "hash" mode, where the routi
Refer to your router's docs for information about hash mode and how to enable it. Here's a non-extensive list of a few popular routers:
- [`react-router`](https://reactrouter.com/en/main/routers/create-hash-router)
- [`react-router`](https://reactrouter.com/api/data-routers/createHashRouter#createhashrouter)
- [`vue-router`](https://router.vuejs.org/guide/essentials/history-mode.html#Hash-Mode)
- [`svelte-spa-router`](https://www.npmjs.com/package/svelte-spa-router#hash-based-routing)
- [`solid-router`](https://github.com/solidjs/solid-router?tab=readme-ov-file#hash-mode-router)
+3 -3
View File
@@ -206,13 +206,13 @@ WXT uses the command `npm pack <package-name>` to download the package. That mea
WXT does not currently support automated publishing for Safari. Safari extensions require a native MacOS or iOS app wrapper, which WXT does not create yet. For now, if you want to publish to Safari, follow this guide:
- [Converting a web extension for Safari](https://developer.apple.com/documentation/safariservices/safari_web_extensions/converting_a_web_extension_for_safari) - "Convert your existing extension to a Safari web extension using Xcodes command-line tool."
- [Packaging a web extension for Safari](https://developer.apple.com/documentation/safariservices/packaging-a-web-extension-for-safari) - "Package your existing extension as a Safari web extension using Xcodes command-line tool."
When running the `safari-web-extension-converter` CLI tool, pass the `.output/safari-mv2` or `.output/safari-mv3` directory, not your source code directory.
When running the `safari-web-extension-packager` CLI tool, pass the `.output/safari-mv2` or `.output/safari-mv3` directory, not your source code directory.
```sh
pnpm wxt build -b safari
xcrun safari-web-extension-converter .output/safari-mv2
xcrun safari-web-extension-packager .output/safari-mv2
```
### Edge Addons
+29
View File
@@ -137,6 +137,35 @@ console.log(config.myModule);
This is very useful when [generating runtime code](#generate-runtime-module).
#### Logging
There are two "correct" ways to add console logs to your modules:
1. Use `wxt.logger` for info, warnings, and errors
2. Install [`obug`](https://www.npmjs.com/package/obug) for debug messages
```ts
import { defineWxtModule } from 'wxt/modules';
import { createDebug } from 'obug';
const debug = createDebug('my-module');
export default defineWxtModule({
setup(wxt) {
wxt.logger.info('Module loaded');
debug('Debug details');
},
});
```
`wxt.logger` is great for formatted, pretty messages that match the rest of WXT's logs.
`obug` makes it easy for devs to enable and filter debug logs when necessary:
```sh
DEBUG=my-module wxt dev
```
#### Add custom entrypoint options
Modules can add custom options to entrypoints by augmenting the entrypoint options types. This allows you to add custom configuration that can be accessed during the build process.
+30
View File
@@ -164,6 +164,36 @@ Both issues have the same cause: the library puts something outside the `ShadowR
Both issues have the same fix: tell the library to put elements inside the `ShadowRoot`, not outside it. See the details above for more information and example fixes for each problem.
## My content script UI looks different on certain websites
If your `createShadowRootUi` looks correct on most sites but appears at the wrong size on others (e.g., Reddit), it's because `rem` unit is relative to the `<html>` element's `font-size`, which lives outside the Shadow DOM. When a website overrides it, your UI scales incorrectly.
The fix is to convert `rem` units to `px` at build time using [`postcss-rem-to-responsive-pixel`](https://www.npmjs.com/package/postcss-rem-to-responsive-pixel). This eliminates the dependency on the host page's root font-size.
1. Install the package:
```sh
bun i -D postcss-rem-to-responsive-pixel
```
2. Configure your PostCSS config:
```js [postcss.config.mjs]
import remToPx from 'postcss-rem-to-responsive-pixel';
export default {
plugins: [
remToPx({
rootValue: 16,
propList: ['*'],
transformUnit: 'px',
}),
],
};
```
See [Issue #678](https://github.com/wxt-dev/wxt/issues/678) for additional context on this behavior.
## Does WXT provide docs for LLMs?
Yes, WXT's documentation provides markdown files based on the [the /llms.txt proposal](https://llmstxt.org/).
+1
View File
@@ -0,0 +1 @@
<svg width="114" height="50" viewBox="0 0 114 50" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path fill="#060B10" d="M0 0h114v51H0z"/><g clip-path="url(#b)" fill="#fff"><path d="M44.445 40.302v-7.695l.153-.16h1.853l.154.16v7.695l-.154.16h-1.853l-.153-.16ZM44.445 17.855v-7.696l.153-.159h1.853l.154.16v7.695l-.154.16h-1.853l-.153-.16ZM38.068 34.892h-.254l-1.275-1.323v-.264l2.388-2.482 1.349.002.18.185v1.4l-2.388 2.482ZM36.54 17.227v-.267l1.274-1.32h.254l2.388 2.479v1.398l-.18.19h-1.349l-2.388-2.48ZM28.154 24.107h10.5l.153.16v1.926l-.153.16h-10.5l-.154-.16v-1.927l.154-.16ZM99.844 24.11l.154.159v1.924l-.154.16H89.19l-.153-.16.77-1.924.154-.16h9.883ZM49 29.24h-1.853l-.153-.16v-4.502c0-.802-.303-1.423-1.234-1.443a24.2 24.2 0 0 0-1.613.025l-.089.092v5.826l-.153.16h-1.853l-.154-.16v-7.695l.154-.16h4.169c1.62 0 2.933 1.364 2.933 3.046v4.81l-.154.159v.002ZM57.974 25.871l-.154.16h-4.79l-.154.16c0 .32.31 1.283 1.546 1.283.463 0 .926-.16 1.082-.481l.154-.16h1.852l.154.16c-.154.962-.926 2.407-3.245 2.407-2.625 0-3.861-1.924-3.861-4.172 0-2.248 1.236-4.171 3.708-4.171 2.472 0 3.708 1.924 3.708 4.171v.643Zm-2.319-1.605c0-.16-.153-1.283-1.39-1.283-1.235 0-1.389 1.124-1.389 1.283l.154.16h2.472l.153-.16ZM62.298 26.833c0 .322.154.481.464.481h1.39l.153.16v1.604l-.154.16h-1.39c-1.389 0-2.625-.643-2.625-2.407v-3.529l-.154-.16H58.9l-.154-.159v-1.605l.154-.16h1.082l.154-.159v-1.443l.154-.16h1.852l.154.16v1.443l.154.16h1.699l.153.16v1.604l-.153.16h-1.7l-.153.159v3.529l.002.002ZM68.015 29.24h-1.853l-.153-.16V18.172l.153-.16h1.853l.154.16v10.907l-.154.16v.002ZM72.186 19.935h-1.852l-.154-.16v-1.604l.154-.16h1.852l.154.16v1.605l-.154.16Zm0 9.305h-1.852l-.154-.16v-7.7l.154-.159h1.852l.154.16v7.7l-.154.16ZM79.449 18.171v1.605l-.154.16h-1.39c-.309 0-.463.159-.463.48v.643l.154.16h1.546l.153.16v1.604l-.153.16h-1.546l-.154.159v5.774l-.153.16h-1.853l-.154-.16v-5.774l-.153-.16h-1.083l-.153-.159v-1.605l.153-.16h1.083l.153-.159v-.643c0-1.764 1.236-2.407 2.626-2.407h1.39l.153.16-.002.002ZM85.163 29.4c-.619 1.605-1.236 2.567-3.398 2.567h-.773l-.154-.16v-1.605l.154-.16h.773c.773 0 .926-.159 1.082-.64v-.16l-2.472-6.257v-1.604l.154-.16h1.39l.153.16 1.853 5.455h.153l1.853-5.455.154-.16h1.39l.153.16v1.604l-2.472 6.417.007-.002Z"/></g><path fill="#fff" d="M0 0v50h15V0z"/><path d="M6.518 45.367v-.774H11v.774H6.518Zm0-1.746c0-.44.098-.784.294-1.032.196-.248.474-.372.834-.372.348 0 .622.13.822.39.2.26.3.628.3 1.104l-.336.018c.004-.608.114-1.062.33-1.362.212-.3.524-.45.936-.45.424 0 .748.146.972.438.22.288.33.686.33 1.194v1.242h-.738v-1.152c0-.268-.05-.488-.15-.66a.498.498 0 0 0-.462-.258c-.224 0-.39.088-.498.264-.108.172-.162.39-.162.654v1.146h-.738V43.68c0-.192-.04-.36-.12-.504a.426.426 0 0 0-.396-.216c-.192 0-.33.068-.414.204a.955.955 0 0 0-.126.51v1.116h-.678v-1.17Zm0-2.925v-.774H9.17c.256 0 .472-.04.648-.12a.88.88 0 0 0 .39-.336.926.926 0 0 0 .126-.486.926.926 0 0 0-.126-.486.845.845 0 0 0-.39-.33 1.497 1.497 0 0 0-.648-.126H6.518v-.75h2.664c.604 0 1.068.146 1.392.438.324.288.486.71.486 1.266 0 .56-.162.984-.486 1.272-.324.288-.788.432-1.392.432H6.518Zm0-5.048v-.774H11v.774H6.518Zm0-2.475V32.4H11v.774H6.518Zm3.75-.546v-2.412H11v2.412h-.732Zm-3.69-3.903v-.773H11v.773H6.578Zm-.06 1.41v-3.6h.732v3.6h-.732Zm0-6.98v-.827l3.684-1.056v.318L6.518 20.43v-.888l3.684-1.122v.318L6.518 17.64v-.798L11 18.18v.81l-4.086 1.254v-.438L11 21.097v.756l-4.482 1.302Zm0-7.385v-.774H11v.774H6.518Zm.06-3.283v-.774H11v.774H6.578Zm-.06 1.41v-3.6h.732v3.6h-.732Zm0-4.7v-.773H11v.774H6.518Zm1.572-.545V6.179h.732V8.65H8.09ZM6.518 6.407v-.774H11v.774H6.518Z" fill="#060B10"/></g><rect x="1" y="1" width="112" height="48" rx="3" stroke="#060B10" stroke-width="2"/><defs><clipPath id="a"><rect width="114" height="50" rx="4" fill="#fff"/></clipPath><clipPath id="b"><path fill="#fff" d="M28 10h72v30.462H28z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="114" height="50" viewBox="0 0 114 50" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><rect width="114" height="50" rx="4" fill="#181A1C"/><path fill="#fff" d="M0 0h114v51H0z"/><g clip-path="url(#b)" fill="#181A1C"><path d="M44.445 40.3v-7.694l.153-.16h1.853l.154.16V40.3l-.154.16h-1.853l-.153-.16ZM44.445 17.854V10.16l.153-.16h1.853l.154.16v7.694l-.154.16h-1.853l-.153-.16ZM38.068 34.89h-.254l-1.275-1.322v-.264l2.388-2.482 1.349.002.18.184v1.4l-2.388 2.483ZM36.54 17.226v-.266l1.274-1.321h.254l2.388 2.48v1.397l-.18.19h-1.349l-2.388-2.48ZM28.154 24.106h10.5l.153.16v1.926l-.153.16h-10.5l-.154-.16v-1.926l.154-.16ZM99.844 24.109l.154.16v1.923l-.154.16H89.19l-.153-.16.77-1.924.154-.16h9.883ZM49 29.24h-1.853l-.153-.16v-4.503c0-.802-.303-1.423-1.234-1.443-.48-.012-1.027 0-1.613.025l-.089.093v5.825l-.153.16h-1.853l-.154-.16v-7.694l.154-.16h4.169c1.62 0 2.933 1.363 2.933 3.045v4.81l-.154.159v.002ZM57.974 25.87l-.154.16h-4.79l-.154.16c0 .32.31 1.283 1.546 1.283.463 0 .926-.16 1.082-.481l.154-.16h1.852l.154.16c-.154.962-.926 2.407-3.245 2.407-2.625 0-3.861-1.924-3.861-4.172 0-2.247 1.236-4.17 3.708-4.17 2.472 0 3.708 1.923 3.708 4.17v.643Zm-2.319-1.604c0-.16-.153-1.284-1.39-1.284-1.235 0-1.389 1.124-1.389 1.284l.154.16h2.472l.153-.16ZM62.298 26.832c0 .322.154.481.464.481h1.39l.153.16v1.604l-.154.16h-1.39c-1.389 0-2.625-.643-2.625-2.407V23.3l-.154-.16H58.9l-.154-.159v-1.604l.154-.16h1.082l.154-.16v-1.442l.154-.16h1.852l.154.16v1.443l.154.16h1.699l.153.159v1.604l-.153.16h-1.7l-.153.16v3.528l.002.002ZM68.015 29.24h-1.853l-.153-.16V18.17l.153-.159h1.853l.154.16v10.906l-.154.16v.002ZM72.186 19.935h-1.852l-.154-.16v-1.604l.154-.16h1.852l.154.16v1.604l-.154.16Zm0 9.304h-1.852l-.154-.16V21.38l.154-.16h1.852l.154.16v7.7l-.154.16ZM79.449 18.17v1.605l-.154.16h-1.39c-.309 0-.463.16-.463.48v.644l.154.16h1.546l.153.159v1.604l-.153.16h-1.546l-.154.16v5.773l-.153.16h-1.853l-.154-.16V23.3l-.153-.16h-1.083l-.153-.159v-1.604l.153-.16h1.083l.153-.16v-.642c0-1.764 1.236-2.407 2.626-2.407h1.39l.153.16-.002.002ZM85.163 29.399c-.619 1.604-1.236 2.566-3.398 2.566h-.773l-.154-.16v-1.604l.154-.16h.773c.773 0 .926-.159 1.082-.64v-.16l-2.472-6.256V21.38l.154-.16h1.39l.153.16 1.853 5.455h.153l1.853-5.455.154-.16h1.39l.153.16v1.605L85.156 29.4l.007-.002Z"/></g><path fill="#181A1C" d="M0 0h15v50H0z"/><path d="M5.518 45.367v-.774H10v.774H5.518Zm0-1.746c0-.44.098-.784.294-1.032.196-.248.474-.372.834-.372.348 0 .622.13.822.39.2.26.3.628.3 1.104l-.336.018c.004-.608.114-1.062.33-1.362.212-.3.524-.45.936-.45.424 0 .748.146.972.438.22.288.33.686.33 1.194v1.242h-.738v-1.152c0-.268-.05-.488-.15-.66a.498.498 0 0 0-.462-.258c-.224 0-.39.088-.498.264-.108.172-.162.39-.162.654v1.146h-.738V43.68c0-.192-.04-.36-.12-.504a.426.426 0 0 0-.396-.216c-.192 0-.33.068-.414.204a.955.955 0 0 0-.126.51v1.116h-.678v-1.17Zm0-2.925v-.774H8.17c.256 0 .472-.04.648-.12a.88.88 0 0 0 .39-.336.926.926 0 0 0 .126-.486.926.926 0 0 0-.126-.486.845.845 0 0 0-.39-.33 1.497 1.497 0 0 0-.648-.126H5.518v-.75h2.664c.604 0 1.068.146 1.392.438.324.288.486.71.486 1.266 0 .56-.162.984-.486 1.272-.324.288-.788.432-1.392.432H5.518Zm0-5.048v-.774H10v.774H5.518Zm0-2.475V32.4H10v.774H5.518Zm3.75-.546v-2.412H10v2.412h-.732Zm-3.69-3.903v-.773H10v.773H5.578Zm-.06 1.41v-3.6h.732v3.6h-.732Zm0-6.98v-.827l3.684-1.056v.318L5.518 20.43v-.888l3.684-1.122v.318L5.518 17.64v-.798L10 18.18v.81l-4.086 1.254v-.438L10 21.097v.756l-4.482 1.302Zm0-7.385v-.774H10v.774H5.518Zm.06-3.283v-.774H10v.774H5.578Zm-.06 1.41v-3.6h.732v3.6h-.732Zm0-4.7v-.773H10v.774H5.518Zm1.572-.545V6.179h.732V8.65H7.09ZM5.518 6.407v-.774H10v.774H5.518Z" fill="#fff"/></g><rect x="1" y="1" width="112" height="48" rx="3" stroke="#181A1C" stroke-width="2"/><defs><clipPath id="a"><rect width="114" height="50" rx="4" fill="#fff"/></clipPath><clipPath id="b"><path fill="#fff" d="M28 10h72v30.46H28z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+29 -14
View File
@@ -17,48 +17,63 @@
"docs:build": "bun run --silent docs:gen && bun run --bun vitepress build docs",
"docs:preview": "bun run --silent docs:gen && bun run --bun vitepress preview docs"
},
"devDependencies": {
"catalog": {
"@aklinker1/buildc": "^1.1.7",
"@aklinker1/check": "^2.4.0",
"@types/bun": "^1.3.5",
"@types/node": "^20.17.6",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@webext-core/fake-browser": "^1.3.4",
"nano-spawn": "^2.0.0",
"oxlint": "^1.63.0",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^6.0.3",
"unocss": "^66.6.8",
"vitest": "^4.1.5",
"vitest-plugin-random-seed": "^1.1.2"
},
"devDependencies": {
"@aklinker1/buildc": "catalog:",
"@aklinker1/check": "catalog:",
"@commitlint/config-conventional": "^20.4.3",
"@commitlint/types": "^20.4.3",
"@types/bun": "^1.3.5",
"@types/bun": "catalog:",
"@types/semver": "^7.7.1",
"@vitest/coverage-v8": "^4.0.18",
"@vitest/coverage-v8": "^4.1.6",
"changelogen": "^0.6.2",
"consola": "^3.4.2",
"cspell": "^9.7.0",
"feed": "^5.2.0",
"markdown-it-footnote": "^4.0.0",
"markdownlint-cli": "^0.48.0",
"nano-spawn": "^2.0.0",
"nano-staged": "^0.8.0",
"nano-spawn": "catalog:",
"nano-staged": "^1.0.2",
"p-map": "^7.0.4",
"pkg-pr-new": "^0.0.66",
"prettier": "^3.8.1",
"prettier-plugin-jsdoc": "^1.8.0",
"semver": "^7.7.4",
"simple-git-hooks": "^2.13.1",
"tinyglobby": "^0.2.15",
"tsdown": "^0.21.0",
"tinyglobby": "^0.2.16",
"tsdown": "catalog:",
"tsx": "4.21.0",
"typedoc": "^0.25.4",
"typedoc-plugin-frontmatter": "^1.3.1",
"typedoc-plugin-markdown": "4.0.0-next.23",
"typedoc-vitepress-theme": "1.0.0-next.3",
"typescript": "^5.9.3",
"typescript": "catalog:",
"vitepress": "^1.6.4",
"vitepress-knowledge": "^0.4.1",
"vitepress-plugin-group-icons": "^1.7.3",
"vitepress-plugin-llms": "^1.12.0",
"vitest-mock-extended": "^3.1.0",
"vue": "^3.5.29",
"vitepress-plugin-llms": "^1.12.2",
"vitest-mock-extended": "^4.0.0",
"vue": "^3.5.34",
"wxt": "workspace:*"
},
"patchedDependencies": {
"markdown-it-footnote@4.0.0": "patches/markdown-it-footnote.patch",
"simple-git-hooks@2.13.1": "patches/simple-git-hooks@2.13.1.patch",
"pkg-pr-new@0.0.66": "patches/pkg-pr-new@0.0.66.patch"
"simple-git-hooks@2.13.1": "patches/simple-git-hooks@2.13.1.patch"
},
"simple-git-hooks": {
"pre-commit": "./node_modules/.bin/nano-staged"
+4 -4
View File
@@ -22,11 +22,11 @@
"wxt": ">=0.20.0"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@aklinker1/buildc": "catalog:",
"@types/ua-parser-js": "^0.7.39",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"wxt": "workspace:*"
},
"repository": {
+6 -7
View File
@@ -52,13 +52,12 @@
"sharp": "^0.34.5"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@types/fs-extra": "^11.0.4",
"oxlint": "^1.51.0",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
"@aklinker1/buildc": "catalog:",
"oxlint": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:",
"wxt": "workspace:*"
}
}
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@wxt-dev/browser",
"description": "Provides a cross-browser API for using extension APIs and types based on @types/chrome",
"version": "0.1.40",
"version": "0.2.0",
"type": "module",
"main": "src/index.mjs",
"types": "src/index.d.ts",
@@ -25,11 +25,11 @@
"src"
],
"devDependencies": {
"@types/chrome": "0.1.40",
"@types/node": "^20.0.0",
"nano-spawn": "^2.0.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
"@types/chrome": "0.2.0",
"@types/node": "catalog:",
"nano-spawn": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"dependencies": {
"@types/filesystem": "*",
+2
View File
@@ -58,6 +58,8 @@ for (const { file, srcPath, destPath } of declarationFileMapping) {
console.log(` ${styleText('dim', '-')} ${styleText('cyan', file)}`);
}
await Bun.$`bun install --ignore-scripts`;
// Done!
console.log(
+1
View File
@@ -1,5 +1,6 @@
/* DO NOT EDIT - generated by scripts/generate.ts */
/* eslint-disable @definitelytyped/strict-export-declare-modifiers */
/* eslint-disable @typescript-eslint/no-wrapper-object-types */
export namespace Browser {
////////////////////
+2 -2
View File
@@ -3,7 +3,7 @@
import { Entry, Log } from "har-format";
declare global {
export type HARFormatEntry = Entry;
export type HARFormatLog = Log;
type HARFormatEntry = Entry;
type HARFormatLog = Log;
}
+1705 -1722
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -26,9 +26,9 @@
],
"devDependencies": {
"@types/chrome": "{{chromeTypesVersion}}",
"@types/node": "^20.0.0",
"nano-spawn": "^2.0.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
"@types/node": "catalog:",
"nano-spawn": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
}
}
+11
View File
@@ -0,0 +1,11 @@
# I18n Benchmarks
These benchmarks must be ran in an actual browser extension environment.
To run them, run `bench/run.ts` and load the `bench/` directory into a browser as an extension. Or just:
```sh
bun --cwd packages/i18n bench
```
Benchmarks are defined in `bench/background.ts`. Add or edit them there.
@@ -0,0 +1,17 @@
{
"simple": {
"message": "Just plain text"
},
"substitution": {
"message": "Includes one substitution: $1"
},
"singular": {
"message": "There is $1 item"
},
"plural": {
"message": "There are $1 items"
},
"wxtPlural": {
"message": "There is $1 item | There are $1 items"
}
}
+85
View File
@@ -0,0 +1,85 @@
import { Bench } from 'tinybench';
import { createI18n } from '../src';
declare const chrome: any;
export const i18n = createI18n();
// Keep service worker alive
setTimeout(() => {
chrome.runtime.getPlatformInfo();
}, 20e3);
(async () => {
const results: Record<string, any> = {};
const runBench = async (bench: Bench) => {
await reportProgress(`[start] ${bench.name}`);
await bench.run();
results[bench.name!] = bench.table();
await reportProgress(`[done] ${bench.name}`);
};
{
const key = 'simple';
const simple = new Bench({ name: 'Simple' })
.add('Vanilla', () => void chrome.i18n.getMessage(key))
.add('WXT I18n', () => void i18n.t(key));
await runBench(simple);
}
{
const key = 'substitution';
const value = 'test';
const substitution = new Bench({ name: 'Substitution' })
.add('Vanilla', () => void chrome.i18n.getMessage(key, [value]))
.add('WXT I18n', () => void i18n.t(key, [value]));
await runBench(substitution);
}
{
const vanillaPluralKey = 'plural';
const wxtKey = 'wxtPlural';
const one = 1;
const pluralSingular = new Bench({
name: 'Plural (singular form)',
})
.add(
'Vanilla',
() => void chrome.i18n.getMessage(vanillaPluralKey, [one]),
)
.add('WXT I18n', () => void i18n.t(wxtKey, one));
await runBench(pluralSingular);
const two = 2;
const vanillaSingularKey = 'singular';
const pluralPlural = new Bench({
name: 'Plural (plural form)',
time: 1000,
})
.add(
'Vanilla',
() => void chrome.i18n.getMessage(vanillaSingularKey, [two]),
)
.add('WXT I18n', () => void i18n.t(wxtKey, two));
await runBench(pluralPlural);
}
// Report results
await reportResults(results);
})();
async function reportProgress(message: string) {
const res = await fetch('http://localhost:3000/progress', {
method: 'POST',
body: message,
});
if (!res.ok) throw Error('Progress report failed...');
}
async function reportResults(results: any) {
const res = await fetch('http://localhost:3000/results', {
method: 'POST',
body: JSON.stringify(results, null, 2),
});
if (!res.ok) throw Error('Results report failed...');
}
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://json.schemastore.org/chrome-manifest",
"name": "WXT I18n Benchmark",
"version": "1.0.0",
"manifest_version": 3,
"default_locale": "en",
"background": {
"type": "module",
"service_worker": "dist/background.js"
}
}
+48
View File
@@ -0,0 +1,48 @@
import { join, relative } from 'node:path';
const distDir = join(import.meta.dir, 'dist');
const backgroundSrc = join(import.meta.dir, 'background.ts');
console.log('\nBuilding benchmark extension...');
await Bun.build({
entrypoints: [backgroundSrc],
outdir: distDir,
format: 'esm',
});
console.log('Done!');
console.log(
`\nInstall the extension from ./${relative(process.cwd(), import.meta.dir)} (or reload it) to run benchmarks.`,
);
console.log('Waiting for benchmark results...');
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*',
};
Bun.serve({
fetch: async (request) => {
// CORS support
if (request.method === 'OPTIONS') {
return new Response(undefined, { headers: CORS_HEADERS });
}
const url = new URL(request.url);
if (url.pathname === '/progress') {
console.log(`\x1b[2m← ${await request.text()}\x1b[0m`);
return new Response(undefined, { status: 202, headers: CORS_HEADERS });
}
if (url.pathname === '/results') {
const body = await request.json();
for (const [name, table] of Object.entries(body)) {
console.log('\n' + name);
console.table(table);
}
setTimeout(() => void process.exit(0));
return new Response(undefined, { status: 202, headers: CORS_HEADERS });
}
return new Response(undefined, { status: 404, headers: CORS_HEADERS });
},
});
+11 -9
View File
@@ -24,6 +24,7 @@
"build": "buildc -- tsdown",
"check": "buildc --deps-only -- check",
"test": "buildc --deps-only -- vitest",
"bench": "buildc --deps-only -- bun run bench/run.ts",
"test:coverage": "bun run test run --coverage",
"prepack": "bun run build"
},
@@ -31,7 +32,7 @@
"@wxt-dev/browser": "workspace:^",
"chokidar": "^5.0.0",
"confbox": "^0.2.4",
"tinyglobby": "^0.2.15"
"tinyglobby": "^0.2.16"
},
"peerDependencies": {
"wxt": ">=0.19.7"
@@ -42,14 +43,15 @@
}
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@types/node": "^20.17.6",
"oxlint": "^1.51.0",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
"vitest-plugin-random-seed": "^1.1.2",
"@aklinker1/buildc": "catalog:",
"@types/node": "catalog:",
"oxlint": "catalog:",
"publint": "catalog:",
"tinybench": "^6.0.1",
"tsdown": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:",
"vitest-plugin-random-seed": "catalog:",
"wxt": "workspace:*"
},
"main": "./dist/index.cjs",
+4 -2
View File
@@ -35,14 +35,16 @@ describe('I18n Types', () => {
});
describe('With type-safety', () => {
const i18n = createI18n<{
type MyStructure = {
simple: { plural: false; substitutions: 0 };
simpleSub1: { plural: false; substitutions: 1 };
simpleSub2: { plural: false; substitutions: 2 };
plural: { plural: true; substitutions: 0 };
pluralSub1: { plural: true; substitutions: 1 };
pluralSub2: { plural: true; substitutions: 2 };
}>();
};
const i18n = createI18n<MyStructure>();
describe('t', () => {
it('should only allow passing valid combinations of arguments', () => {
+6 -11
View File
@@ -1,16 +1,11 @@
/** @module @wxt-dev/i18n */
import {
I18nStructure,
DefaultI18nStructure,
I18n,
Substitution,
} from './types';
import { I18nStructure, I18n, Substitution, UntypedI18n } from './types';
import { browser } from '@wxt-dev/browser';
export function createI18n<
T extends I18nStructure = DefaultI18nStructure,
>(): I18n<T> {
const t = (key: string, ...args: any[]) => {
export function createI18n(): UntypedI18n;
export function createI18n<T extends I18nStructure>(): I18n<T>;
export function createI18n(): UntypedI18n {
const t: UntypedI18n['t'] = (key: string, ...args: unknown[]) => {
// Resolve args
let sub: Substitution[] | undefined;
let count: number | undefined;
@@ -64,5 +59,5 @@ export function createI18n<
}
};
return { t } as I18n<T>;
return { t };
}
+14 -9
View File
@@ -3,13 +3,22 @@ export interface I18nFeatures {
substitutions: SubstitutionCount;
}
export type I18nStructure = {
[K: string]: I18nFeatures;
export interface UntypedI18n {
t: UntypedTFunction;
}
export type UntypedTFunction = {
(key: string): string;
(key: string, substitutions: Substitution[]): string;
(key: string, n: number): string;
(key: string, n: number, substitutions: Substitution[]): string;
};
export type DefaultI18nStructure = {
[K: string]: any;
};
export type I18nStructure = Record<string, I18nFeatures>;
export interface I18n<T extends I18nStructure> {
t: TFunction<T>;
}
// prettier-ignore
export type SubstitutionTuple<T extends SubstitutionCount> =
@@ -66,10 +75,6 @@ export type TFunction<T extends I18nStructure> = {
): string;
};
export interface I18n<T extends DefaultI18nStructure> {
t: TFunction<T>;
}
export type Substitution = string | number;
type SubstitutionCount = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
+1 -1
View File
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node"]
"types": ["bun"]
},
"exclude": ["node_modules/**", "dist/**"]
}
+5 -4
View File
@@ -15,10 +15,11 @@
"@wxt-dev/browser": "workspace:^"
},
"devDependencies": {
"oxlint": "^1.51.0",
"publint": "^0.3.18",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
"oxlint": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"repository": {
"type": "git",
+6 -5
View File
@@ -55,13 +55,14 @@
"@vitejs/plugin-react": "^4.4.1 || ^5.0.0 || ^6.0.0"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"publint": "^0.3.18",
"@aklinker1/buildc": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"publint": "catalog:",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"typescript": "^5.9.3",
"tsdown": "catalog:",
"typescript": "catalog:",
"vite": "^7.0.0",
"wxt": "workspace:*"
}
+4 -4
View File
@@ -54,11 +54,11 @@
"vite-plugin-solid": "^2.11.10"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"publint": "^0.3.18",
"@aklinker1/buildc": "catalog:",
"publint": "catalog:",
"solid-js": "^1.9.11",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"tsdown": "catalog:",
"typescript": "catalog:",
"wxt": "workspace:*"
}
}
+4 -4
View File
@@ -53,10 +53,10 @@
"@sveltejs/vite-plugin-svelte": ">=4"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"@aklinker1/buildc": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"wxt": "workspace:*"
}
}
+4 -4
View File
@@ -52,10 +52,10 @@
"@vitejs/plugin-vue": "^5.2.3 || ^6.0.0"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"@aklinker1/buildc": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"wxt": "workspace:*"
}
}
+10 -7
View File
@@ -29,12 +29,12 @@
"prepack": "bun run build"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"oxlint": "^1.51.0",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
"@aklinker1/buildc": "catalog:",
"oxlint": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"main": "dist/index.mjs",
"types": "dist/index.d.mts",
@@ -46,5 +46,8 @@
},
"files": [
"dist"
]
],
"dependencies": {
"obug": "^2.1.1"
}
}
+26 -6
View File
@@ -25,6 +25,7 @@ describe('Options', () => {
describe('extensionDir', () => {
it('should default to the current working directory', async () => {
const actual = await resolveRunOptions({});
expect(actual).toMatchObject<Partial<ResolvedRunOptions>>({
extensionDir: process.cwd(),
});
@@ -34,17 +35,27 @@ describe('Options', () => {
const actual = await resolveRunOptions({
extensionDir: './path/to/extension',
});
expect(actual).toMatchObject<Partial<ResolvedRunOptions>>({
extensionDir: resolve(process.cwd(), './path/to/extension'),
});
});
it('should use absolute paths as-is', async () => {
const actual = await resolveRunOptions({
extensionDir: '/abs/path/to/extension2',
});
expect(actual).toMatchObject<Partial<ResolvedRunOptions>>({
extensionDir: resolve('/abs/path/to/extension2'),
});
});
});
describe('target', () => {
it('should be "chrome" by default', async () => {
const actual = await resolveRunOptions({
extensionDir: 'path/to/extension',
});
const actual = await resolveRunOptions({});
expect(actual).toMatchObject<Partial<ResolvedRunOptions>>({
target: 'chrome',
});
@@ -52,12 +63,12 @@ describe('Options', () => {
it('should be what is passed in', async () => {
const actual = await resolveRunOptions({
extensionDir: 'path/to/extension',
target: 'custom',
browserBinaries: {
custom: '/path/to/custom/browser',
},
});
expect(actual).toMatchObject<Partial<ResolvedRunOptions>>({
target: 'custom',
});
@@ -65,9 +76,9 @@ describe('Options', () => {
it('should throw an error if the target binary could not be found', async () => {
const actual = resolveRunOptions({
extensionDir: 'path/to/extension',
target: 'custom',
});
await expect(actual).rejects.toThrow('Could not find "custom" binary.');
});
});
@@ -83,7 +94,6 @@ describe('Options', () => {
? 'C:\\path\\to\\custom\\browser.exe'
: path;
const actual = await resolveRunOptions({
extensionDir: 'path/to/extension',
target: 'custom',
browserBinaries: {
custom: path,
@@ -98,9 +108,11 @@ describe('Options', () => {
describe('chromiumArgs', () => {
it('should log a warning when --user-data-dir is passed in', async () => {
const warnSpy = vi.spyOn(console, 'warn');
await resolveRunOptions({
chromiumArgs: ['--user-data-dir=some/custom/path'],
});
expect(warnSpy).toBeCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining(
@@ -111,9 +123,11 @@ describe('Options', () => {
it('should log a warning when --remote-debugging-port is passed in', async () => {
const warnSpy = vi.spyOn(console, 'warn');
await resolveRunOptions({
chromiumArgs: ['--remote-debugging-port=9222'],
});
expect(warnSpy).toBeCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining(
@@ -126,6 +140,7 @@ describe('Options', () => {
const actual = await resolveRunOptions({
chromiumArgs: ['--window-size=1920,1080'],
});
expect(actual.chromiumArgs).toEqual([
// Defaults
'--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculateNativeWinOcclusion,InterestFeedContentSuggestions,CertificateTransparencyComponentUpdater,AutofillServerCommunication,PrivacySandboxSettings4',
@@ -161,9 +176,11 @@ describe('Options', () => {
describe('firefoxArgs', () => {
it('should log a warning when --profile is passed in', async () => {
const warnSpy = vi.spyOn(console, 'warn');
await resolveRunOptions({
firefoxArgs: ['--profile=some/custom/path'],
});
expect(warnSpy).toBeCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining('Custom Firefox --profile argument ignored'),
@@ -172,9 +189,11 @@ describe('Options', () => {
it('should log a warning when --remote-debugging-port is passed in', async () => {
const warnSpy = vi.spyOn(console, 'warn');
await resolveRunOptions({
firefoxArgs: ['--remote-debugging-port=9222'],
});
expect(warnSpy).toBeCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining(
@@ -187,6 +206,7 @@ describe('Options', () => {
const actual = await resolveRunOptions({
firefoxArgs: ['--window-size=1920,1080'],
});
expect(actual.firefoxArgs).toEqual([
// Defaults
'--new-instance',
+10 -10
View File
@@ -1,7 +1,7 @@
import { openWebSocket } from './web-socket';
import { debug } from './debug';
import { runnerDebug } from './debug';
const debugBidi = debug.scoped('bidi');
const debug = runnerDebug.extend('bidi');
export interface BidiConnection extends Disposable {
send<T>(method: string, params: any, timeout?: number): Promise<T>;
@@ -12,10 +12,10 @@ export async function createBidiConnection(
baseUrl: string,
): Promise<BidiConnection> {
const url = new URL('/session', baseUrl);
debugBidi('Connecting to BiDi server @', url.href);
debug('Connecting to BiDi server @', url.href);
const webSocket = await openWebSocket(url.href);
debugBidi('Connected');
debug('Connected');
let requestId = 0;
@@ -23,7 +23,7 @@ export async function createBidiConnection(
send(method, params, timeout = 10e3) {
const id = ++requestId;
const command = { id, method, params };
debugBidi('Sending command:', command);
debug('Sending command:', command);
return new Promise((resolve, reject) => {
const cleanup = () => {
@@ -43,7 +43,7 @@ export async function createBidiConnection(
const onMessage = (event: MessageEvent) => {
const data = JSON.parse(event.data);
if (data.id === id) {
debugBidi('Received response:', data);
debug('Received response:', data);
cleanup();
if (data.type === 'success') resolve(data.result);
else reject(Error(data.message, { cause: data }));
@@ -62,14 +62,14 @@ export async function createBidiConnection(
},
close() {
debugBidi('Closing connection...');
debug('Closing connection...');
webSocket.close();
debugBidi('Closed connection');
debug('Closed connection');
},
[Symbol.dispose]() {
debugBidi('Disposing connection...');
debug('Disposing connection...');
webSocket.close();
debugBidi('Disposed connection');
debug('Disposed connection');
},
};
}
+2 -1
View File
@@ -135,11 +135,12 @@ export const KNOWN_BROWSER_PATHS: Record<
*/
export const FALLBACK_TARGETS: Partial<Record<KnownTarget, KnownTarget[]>> = {
chrome: [
'arc',
'chromium',
'chrome-canary',
'chrome-beta',
'chrome-dev',
'brave',
'arc',
'dia',
'edge',
'edge-canary',
+4 -4
View File
@@ -1,8 +1,8 @@
import type { ChildProcess } from 'node:child_process';
import type { Readable, Writable } from 'node:stream';
import { debug } from './debug';
import { runnerDebug } from './debug';
const debugCdp = debug.scoped('cdp');
const debug = runnerDebug.extend('cdp');
export interface CDPConnection extends Disposable {
send<T>(method: string, params: any, timeout?: number): Promise<T>;
@@ -21,7 +21,7 @@ export function createCdpConnection(
send(method, params, timeout = 10e3) {
const id = ++requestId;
const command = { id, method, params };
debugCdp('Sending command:', command);
debug('Sending command:', command);
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
@@ -35,7 +35,7 @@ export function createCdpConnection(
if (res.id !== id) return;
debugCdp('Received response:', res);
debug('Received response:', res);
clearTimeout(timer);
outputStream.removeListener('data', onData);
+2 -23
View File
@@ -1,24 +1,3 @@
export interface Debug {
(...args: any[]): void;
scoped: (scope: string) => Debug;
}
import { createDebug } from 'obug';
function createDebug(scopes: string[]): Debug {
const debug = (...args: any[]) => {
const scope = scopes.join(':');
if (
process.env.DEBUG === '1' ||
process.env.DEBUG === 'true' ||
scope.startsWith(process.env.DEBUG ?? '@NOT')
) {
const params = scope ? [`\x1b[31m${scope}\x1b[0m`, ...args] : args;
console.log(...params);
}
};
debug.scoped = (scope: string) => createDebug([...scopes, scope]);
return debug;
}
export const debug = createDebug(['@wxt-dev/runner']);
export const runnerDebug = createDebug('wxt:runner', { color: 31 });
+4 -4
View File
@@ -6,11 +6,11 @@ import {
} from './browser-paths';
import { resolve, join } from 'node:path';
import { homedir, tmpdir } from 'node:os';
import { debug } from './debug';
import { runnerDebug } from './debug';
import { mkdtemp, open } from 'node:fs/promises';
import { styleText } from 'node:util';
const debugOptions = debug.scoped('options');
const debug = runnerDebug.extend('options');
export type UnknownTarget = string & {};
export type Target = KnownTarget | UnknownTarget;
@@ -79,7 +79,7 @@ export type ResolvedRunOptions = {
export async function resolveRunOptions(
options: RunOptions | undefined,
): Promise<ResolvedRunOptions> {
debugOptions('User options:', options);
debug('User options:', options);
const target = options?.target || 'chrome';
@@ -126,7 +126,7 @@ export async function resolveRunOptions(
firefoxRemoteDebuggingPort,
target,
};
debugOptions('Resolved options:', resolved);
debug('Resolved options:', resolved);
return resolved;
}
+7 -7
View File
@@ -1,4 +1,4 @@
import { debug } from './debug';
import { runnerDebug } from './debug';
import {
resolveRunOptions,
type ResolvedRunOptions,
@@ -8,8 +8,8 @@ import { spawn } from 'node:child_process';
import { installChromium, installFirefox } from './install';
import { promiseWithResolvers } from './promises';
const debugFirefox = debug.scoped('firefox');
const debugChrome = debug.scoped('chrome');
const debugFirefox = runnerDebug.extend('firefox');
const debugChrome = runnerDebug.extend('chrome');
export interface Runner {
stop(): void;
@@ -44,7 +44,7 @@ async function runFirefox(options: ResolvedRunOptions): Promise<Runner> {
shell: true,
},
);
const debugFirefoxStderr = debugFirefox.scoped('stderr');
const debugFirefoxStderr = debugFirefox.extend('stderr');
browserProcess.stderr.on('data', (data: string) => {
const message = data.toString().trim();
debugFirefoxStderr(message);
@@ -54,7 +54,7 @@ async function runFirefox(options: ResolvedRunOptions): Promise<Runner> {
urlRes.resolve(message.slice(28));
}
});
const debugFirefoxStdout = debugFirefox.scoped('stdout');
const debugFirefoxStdout = debugFirefox.extend('stdout');
browserProcess.stdout.on('data', (data: string) => {
const message = data.toString().trim();
debugFirefoxStdout(message);
@@ -85,7 +85,7 @@ async function runChromium(options: ResolvedRunOptions): Promise<Runner> {
opened.reject(Error('Timed out after 10s waiting for browser to open.'));
}, 10e3);
const debugChromeStderr = debugChrome.scoped('stderr');
const debugChromeStderr = debugChrome.extend('stderr');
browserProcess.stderr!.on('data', (data: string) => {
const message = data.toString().trim();
debugChromeStderr(message);
@@ -96,7 +96,7 @@ async function runChromium(options: ResolvedRunOptions): Promise<Runner> {
opened.resolve();
}
});
const debugChromeStdout = debugChrome.scoped('stdout');
const debugChromeStdout = debugChrome.extend('stdout');
browserProcess.stdout!.on('data', (data: string) => {
const message = data.toString().trim();
debugChromeStdout(message);
+7 -7
View File
@@ -37,13 +37,13 @@
"dequal": "^2.0.3"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@webext-core/fake-browser": "^1.3.4",
"oxlint": "^1.51.0",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
"@aklinker1/buildc": "catalog:",
"@webext-core/fake-browser": "catalog:",
"oxlint": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
+7 -7
View File
@@ -43,16 +43,16 @@
"wxt": ">=0.19.0"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"oxlint": "^1.51.0",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"unocss": "^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0",
"@aklinker1/buildc": "catalog:",
"oxlint": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"unocss": "catalog:",
"wxt": "workspace:*"
},
"dependencies": {
"defu": "^6.1.4",
"tinyglobby": "^0.2.15"
"tinyglobby": "^0.2.16"
}
}
+1 -2
View File
@@ -52,8 +52,7 @@ export interface UnoCSSOptions<Theme extends object = object> {
* it from specific builds.
*
* @example
* {undefined} ('popup',
* 'options');
* ['popup', 'options'];
*
* @default [ ]
*/
+4 -4
View File
@@ -48,11 +48,11 @@
"wxt": ">=0.20.0"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@aklinker1/buildc": "catalog:",
"@types/webextension-polyfill": "^0.12.5",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"webextension-polyfill": "^0.12.0",
"wxt": "workspace:*"
}
+8 -8
View File
@@ -25,17 +25,17 @@
"react-dom": "^19.2.4"
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@aklinker1/check": "^2.2.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@aklinker1/buildc": "catalog:",
"@aklinker1/check": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@wxt-dev/auto-icons": "workspace:*",
"@wxt-dev/unocss": "workspace:*",
"sass": "^1.97.3",
"typescript": "^5.9.3",
"unocss": "^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0",
"vitest": "^4.0.18",
"vitest-plugin-random-seed": "^1.1.2",
"typescript": "catalog:",
"unocss": "catalog:",
"vitest": "catalog:",
"vitest-plugin-random-seed": "catalog:",
"wxt": "workspace:*"
},
"buildc": {
@@ -1,3 +1,4 @@
// @ts-expect-error: URL imports not typed
import 'url:https://code.jquery.com/jquery-3.7.1.slim.min.js';
console.log(browser.runtime.id);
+143
View File
@@ -1,5 +1,148 @@
# Changelog
## v0.20.27
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.26...wxt-v0.20.27)
### 🚀 Enhancements
- Add warning for missing Firefox extension ID in manifest ([#2293](https://github.com/wxt-dev/wxt/pull/2293))
### 🩹 Fixes
- Correct broken link at react router in createhashrouter ([#2411](https://github.com/wxt-dev/wxt/pull/2411))
- Rename `ManifestPermissions` to `ManifestPermission` of types.ts in `wxt` package ([#2430](https://github.com/wxt-dev/wxt/pull/2430))
### 📖 Documentation
- Add Telsia extension to Showcase ([#2368](https://github.com/wxt-dev/wxt/pull/2368))
- Add warning about rem unit inheritance in Shadow Root UI ([#2330](https://github.com/wxt-dev/wxt/pull/2330))
- Added "Sound Booster & Equalizer" to showcase ([#2381](https://github.com/wxt-dev/wxt/pull/2381))
- Clarify SPA navigation setup with locationWatcher.run() ([#2380](https://github.com/wxt-dev/wxt/pull/2380))
- Self-host Netlify footer badge ([#2386](https://github.com/wxt-dev/wxt/pull/2386))
- Added "Link Later" to showcase" ([#2392](https://github.com/wxt-dev/wxt/pull/2392))
- Added Property Track to showcase ([#2404](https://github.com/wxt-dev/wxt/pull/2404))
- Added "WageSlav3" to showcase ([#2405](https://github.com/wxt-dev/wxt/pull/2405))
- Added "LinkedIn Games Solver" to showcase ([#2410](https://github.com/wxt-dev/wxt/pull/2410))
- Added NiceTab firefoxslug to extension showcase ([#2417](https://github.com/wxt-dev/wxt/pull/2417))
- Added 4 extensions to the homepage ([#2419](https://github.com/wxt-dev/wxt/pull/2419))
- Added Hayami to showcase ([#2422](https://github.com/wxt-dev/wxt/pull/2422))
- Added anylang translator to showcase ([#2426](https://github.com/wxt-dev/wxt/pull/2426))
### 🏡 Chore
- **deps:** Bump c12 from 3.3.3 to 3.3.4 ([#2364](https://github.com/wxt-dev/wxt/pull/2364))
- **deps:** Bump filesize from 11.0.15 to 11.0.17 ([#2363](https://github.com/wxt-dev/wxt/pull/2363))
- **deps:** Bump tinyglobby from 0.2.15 to 0.2.16 ([#2366](https://github.com/wxt-dev/wxt/pull/2366))
- Add Google Cloud Skills Boost - Helper extension to showcase ([#2401](https://github.com/wxt-dev/wxt/pull/2401))
### ❤️ Contributors
- Patryk Kuniczak ([@PatrykKuniczak](https://github.com/PatrykKuniczak))
- RenovZ ([@RenovZ](https://github.com/RenovZ))
- Nicholas Yoannou ([@nicholasyoannou](https://github.com/nicholasyoannou))
- Avi ([@avi12](https://github.com/avi12))
- Peter Wong <www121ppp@gmail.com>
- ChamikaUluwatta ([@ChamikaUluwatta](https://github.com/ChamikaUluwatta))
- Muhammed Mustafa AKŞAM ([@muhammedaksam](https://github.com/muhammedaksam))
- Slovakian ([@slovakian](https://github.com/slovakian))
- Oliver Benns ([@oliverbenns](https://github.com/oliverbenns))
- David Nguyen <h250694@gmail.com>
- Esteban Vera <minos_93105@hotmail.com>
- Sean Kenneth Doherty <Smaster7772@gmail.com>
- Muhammad Umer Farooq ([@mrumerf](https://github.com/mrumerf))
- Muzammil ([@oyzamil](https://github.com/oyzamil))
- Aniket Lodh ([@Aniket-lodh](https://github.com/Aniket-lodh))
- George Forse <george@gforse.uk>
- Shubhrajit Sadhukhan <champa.281982@gmail.com>
## v0.20.26
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.25...wxt-v0.20.26)
### 🚀 Enhancements
- Add `default_state` option to popup actions ([#2010](https://github.com/wxt-dev/wxt/pull/2010))
- Add content script `noScriptStartedPostMessage` option ([#2265](https://github.com/wxt-dev/wxt/pull/2265))
### 🩹 Fixes
- Use `manifestVersion` from CLI during manifest generation ([#2306](https://github.com/wxt-dev/wxt/pull/2306))
- Modify command to support variadic positional args ([#2317](https://github.com/wxt-dev/wxt/pull/2317))
- Bump `publish-browser-extension` to `v4.0.5`, to resolve Chrome Web Store submission issue ([#2331](https://github.com/wxt-dev/wxt/pull/2331))
- Avoid errors when files are removed during build ([#2343](https://github.com/wxt-dev/wxt/pull/2343))
- **manifest:** Exclude `open_in_tab` from `options_ui` for Safari ([#2311](https://github.com/wxt-dev/wxt/pull/2311))
### 📖 Documentation
- **wxt-modules:** Add logging examples/best practices ([57e3748d](https://github.com/wxt-dev/wxt/commit/57e3748d))
- Correct entrypoints icon example code ([#2302](https://github.com/wxt-dev/wxt/pull/2302))
- Add types for components ([#2273](https://github.com/wxt-dev/wxt/pull/2273))
- Fix JSDoc example formatting ([2a8ec0d7](https://github.com/wxt-dev/wxt/commit/2a8ec0d7))
- Remove unused code ([#2275](https://github.com/wxt-dev/wxt/pull/2275))
- Fix horizontal scrollbar displaying on landing page ([#2329](https://github.com/wxt-dev/wxt/pull/2329))
- Add Mimik extension to the list of extensions ([#2319](https://github.com/wxt-dev/wxt/pull/2319))
- Added modrinth extras and pi-hole in one to showcase ([#2337](https://github.com/wxt-dev/wxt/pull/2337))
- Added "QIE Wallet" to showcase ([#2341](https://github.com/wxt-dev/wxt/pull/2341))
- Update Safari publishing instructions to match Apple docs ([#2314](https://github.com/wxt-dev/wxt/pull/2314))
- Add "Redmine Time Tracking" to extension showcase ([#2312](https://github.com/wxt-dev/wxt/pull/2312))
### 🏡 Chore
- More JSDoc fixes ([9b59f38c](https://github.com/wxt-dev/wxt/commit/9b59f38c))
- Move `createFileReloader` into it's own file ([#2307](https://github.com/wxt-dev/wxt/pull/2307))
- Remove ts-expect-error that are no longer needed ([#2344](https://github.com/wxt-dev/wxt/pull/2344))
- **deps-dev:** Bump typescript from 5.9.3 to 6.0.3 ([#2325](https://github.com/wxt-dev/wxt/pull/2325))
- **deps-dev:** Bump oxlint from 1.59.0 to 1.63.0 ([#2356](https://github.com/wxt-dev/wxt/pull/2356))
- Use `catalog:` for dev dependencies ([#2357](https://github.com/wxt-dev/wxt/pull/2357))
### ❤️ Contributors
- Jonathan Viney ([@jviney](https://github.com/jviney))
- Rxliuli ([@rxliuli](https://github.com/rxliuli))
- CrawlerCode ([@CrawlerCode](https://github.com/CrawlerCode))
- Thomas Howlett ([@howlettt](https://github.com/howlettt))
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Brian Zhou ([@aiktb](https://github.com/aiktb))
- Soaib Haque ([@SoaibHaque](https://github.com/SoaibHaque))
- Arthur <creeperkatze.dev@gmail.com>
- Luis Ortiz ([@KiryuuLight](https://github.com/KiryuuLight))
- Patryk Kuniczak ([@PatrykKuniczak](https://github.com/PatrykKuniczak))
- Laplace-anon ([@laplace-anon](https://github.com/laplace-anon))
- Kairi ([@kairi003](https://github.com/kairi003))
## v0.20.25
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.24...wxt-v0.20.25)
### 🩹 Fixes
- Port change in dev server on reload ([#2283](https://github.com/wxt-dev/wxt/pull/2283))
### 📖 Documentation
- Update `permissions` section with per-browser warning ([#2284](https://github.com/wxt-dev/wxt/pull/2284))
### 🏡 Chore
- Increase PNPM test timeout ([3db3eac6](https://github.com/wxt-dev/wxt/commit/3db3eac6))
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
## v0.20.24
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.23...wxt-v0.20.24)
### 🩹 Fixes
- Correct `actionKey` for Firefox in MV3 ([#2274](https://github.com/wxt-dev/wxt/pull/2274))
### ❤️ Contributors
- John Wong <john_wong@live.com>
## v0.20.23
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.22...wxt-v0.20.23)
+2 -14
View File
@@ -1,17 +1,5 @@
import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
import { createServer as createNetServer } from 'node:net';
/** Starts a TCP server on the given port and returns a cleanup function. */
function occupyPort(port: number): Promise<() => Promise<void>> {
return new Promise((resolve, reject) => {
const srv = createNetServer();
srv.listen(port, 'localhost', () => {
resolve(() => new Promise<void>((res) => srv.close(() => res())));
});
srv.on('error', reject);
});
}
import { describe, expect, it } from 'vitest';
import { TestProject, occupyPort } from '../utils';
describe('Dev Mode', () => {
it('should not change ports when restarting the server', async () => {
+25
View File
@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest';
import { registerWxt, wxt } from '../../src/core/wxt';
import { TestProject, occupyPort } from '../utils';
describe('WXT Global', () => {
describe('reloadConfig', () => {
it('should not change dev server ports when reloading config, even if the port is in-use', async () => {
const project = new TestProject();
await registerWxt('serve', { root: project.root });
const firstPort = wxt.config.dev.server!.port;
expect(firstPort).toBeDefined();
const cleanup = await occupyPort(firstPort);
try {
await wxt.reloadConfig();
const secondPort = wxt.config.dev.server?.port;
expect(secondPort).toBe(firstPort);
} finally {
cleanup();
}
});
});
});
+12
View File
@@ -1,6 +1,7 @@
import merge from 'lodash.merge';
import spawn, { Subprocess } from 'nano-spawn';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { createServer as createNetServer } from 'node:net';
import { dirname, relative, resolve } from 'path';
import { glob } from 'tinyglobby';
import {
@@ -204,3 +205,14 @@ export class TestProject {
});
}
}
/** Starts a TCP server on the given port and returns a cleanup function. */
export function occupyPort(port: number): Promise<() => Promise<void>> {
return new Promise((resolve, reject) => {
const srv = createNetServer();
srv.listen(port, 'localhost', () => {
resolve(() => new Promise<void>((res) => srv.close(() => res())));
});
srv.on('error', reject);
});
}
+1 -1
View File
@@ -1,4 +1,4 @@
// oxlint-disable-next-line eslint(no-shadow-restricted-names)
// oxlint-disable-next-line no-shadow-restricted-names
declare namespace globalThis {
var __ENTRYPOINT__: string;
}
+14 -15
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.20.23",
"version": "0.20.27",
"description": "⚡ Next-gen Web Extension Framework",
"license": "MIT",
"scripts": {
@@ -24,7 +24,7 @@
"@wxt-dev/browser": "workspace:^",
"@wxt-dev/storage": "workspace:^1.0.0",
"async-mutex": "^0.5.0",
"c12": "^3.3.3",
"c12": "^3.3.4",
"cac": "^6.7.14 || ^7.0.0",
"chokidar": "^5.0.0",
"ci-info": "^4.4.0",
@@ -32,7 +32,7 @@
"defu": "^6.1.4",
"dotenv-expand": "^12.0.3",
"esbuild": "^0.27.1",
"filesize": "^11.0.15",
"filesize": "^11.0.17",
"get-port-please": "^3.2.0",
"giget": "^1.2.3 || ^2.0.0 || ^3.0.0",
"hookable": "^6.1.0",
@@ -51,9 +51,9 @@
"perfect-debounce": "^2.1.0",
"picomatch": "^4.0.3",
"prompts": "^2.4.2",
"publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.4",
"publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.5",
"scule": "^1.3.0",
"tinyglobby": "^0.2.15",
"tinyglobby": "^0.2.16",
"unimport": "^3.13.1 || ^4.0.0 || ^5.0.0 || ^6.0.0",
"vite": "^5.4.19 || ^6.3.4 || ^7.0.0 || ^8.0.0-0",
"vite-node": "^3.2.4 || ^5.0.0 || ^6.0.0",
@@ -68,12 +68,11 @@
}
},
"devDependencies": {
"@aklinker1/buildc": "^1.1.7",
"@aklinker1/buildc": "catalog:",
"@faker-js/faker": "^10.3.0",
"@types/bun": "^1.3.5",
"@types/fs-extra": "^11.0.4",
"@types/bun": "catalog:",
"@types/lodash.merge": "^4.6.9",
"@types/node": "^20.17.6",
"@types/node": "catalog:",
"@types/normalize-path": "^3.0.2",
"@types/picomatch": "^4.0.2",
"@types/prompts": "^2.4.9",
@@ -81,12 +80,12 @@
"extract-zip": "^2.0.1",
"happy-dom": "^20.8.3",
"lodash.merge": "^4.6.2",
"oxlint": "^1.51.0",
"publint": "^0.3.18",
"tsdown": "^0.21.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18",
"vitest-plugin-random-seed": "^1.1.2"
"oxlint": "catalog:",
"publint": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:",
"vitest-plugin-random-seed": "catalog:"
},
"repository": {
"type": "git",
+5 -1
View File
@@ -82,8 +82,12 @@ export function createAliasedCommand(
bin: string,
docsUrl: string,
) {
// Declare a variadic positional arg so cac forwards subcommands like `wxt
// submit init` instead of rejecting them as unused args. `.allowUnknownOptions`
// only relaxes flag checks, not positional args. Required since cac@7, which
// throws on unused positional args (see cacjs/cac#135).
const aliasedCommand = base
.command(name, `Alias for ${alias} (${docsUrl})`)
.command(`${name} [...args]`, `Alias for ${alias} (${docsUrl})`)
.allowUnknownOptions()
.action(async () => {
try {
+6 -175
View File
@@ -1,35 +1,15 @@
import { debounce } from 'perfect-debounce';
import chokidar from 'chokidar';
import {
BuildStepOutput,
EntrypointGroup,
InlineConfig,
ServerInfo,
WxtDevServer,
} from '../types';
import { getEntrypointBundlePath, isHtmlEntrypoint } from './utils/entrypoints';
import {
getContentScriptCssFiles,
getContentScriptsCssMap,
} from './utils/manifest';
import {
internalBuild,
detectDevChanges,
rebuild,
findEntrypoints,
} from './utils/building';
import { InlineConfig, ServerInfo, WxtDevServer } from '../types';
import { internalBuild } from './utils/building';
import { createExtensionRunner } from './runners';
import { Mutex } from 'async-mutex';
import { relative } from 'node:path';
import { deinitWxtModules, initWxtModules, registerWxt, wxt } from './wxt';
import { unnormalizePath } from './utils/paths';
import {
getContentScriptJs,
mapWxtOptionsToRegisteredContentScript,
} from './utils/content-scripts';
import { createKeyboardShortcuts } from './keyboard-shortcuts';
import { isBabelSyntaxError, logBabelSyntaxError } from './utils/syntax-errors';
import { styleText } from 'node:util';
import {
createFileReloader,
reloadContentScripts,
} from './utils/create-file-reloader';
/**
* Creates a dev server and pre-builds all the files that need to exist before
@@ -199,155 +179,6 @@ async function createServerInternal(): Promise<WxtDevServer> {
return server;
}
/**
* Returns a function responsible for reloading different parts of the extension
* when a file changes.
*/
function createFileReloader(server: WxtDevServer) {
const fileChangedMutex = new Mutex();
const changeQueue: Array<[string, string]> = [];
const cb = async (event: string, path: string) => {
changeQueue.push([event, path]);
const reloading = fileChangedMutex.runExclusive(async () => {
if (server.currentOutput == null) return;
const fileChanges = changeQueue
.splice(0, changeQueue.length)
.map(([_, file]) => file);
if (fileChanges.length === 0) return;
await wxt.reloadConfig();
const changes = detectDevChanges(fileChanges, server.currentOutput);
if (changes.type === 'no-change') return;
if (changes.type === 'full-restart') {
wxt.logger.info('Config changed, restarting server...');
server.restart();
return;
}
if (changes.type === 'browser-restart') {
wxt.logger.info('Runner config changed, restarting browser...');
server.restartBrowser();
return;
}
// Log the entrypoints that were effected
wxt.logger.info(
`Changed: ${Array.from(new Set(fileChanges))
.map((file) => styleText('dim', relative(wxt.config.root, file)))
.join(', ')}`,
);
// Rebuild entrypoints on change
const allEntrypoints = await findEntrypoints();
try {
const { output: newOutput } = await rebuild(
allEntrypoints,
// TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
changes.rebuildGroups,
changes.cachedOutput,
);
server.currentOutput = newOutput;
// Perform reloads
switch (changes.type) {
case 'extension-reload':
server.reloadExtension();
wxt.logger.success(`Reloaded extension`);
break;
case 'html-reload':
const { reloadedNames } = reloadHtmlPages(
changes.rebuildGroups,
server,
);
wxt.logger.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
break;
case 'content-script-reload':
reloadContentScripts(changes.changedSteps, server);
const rebuiltNames = changes.rebuildGroups
.flat()
.map((entry) => entry.name);
wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
break;
}
} catch {
// Catch build errors instead of crashing. Don't log error either, builder should have already logged it
}
});
await reloading.catch((error) => {
if (!isBabelSyntaxError(error)) {
throw error;
}
// Log syntax errors without crashing the server.
logBabelSyntaxError(error);
});
};
return debounce(cb, wxt.config.dev.server!.watchDebounce, {
leading: true,
trailing: false,
});
}
/**
* From the server, tell the client to reload content scripts from the provided
* build step outputs.
*/
function reloadContentScripts(steps: BuildStepOutput[], server: WxtDevServer) {
if (wxt.config.manifestVersion === 3) {
steps.forEach((step) => {
if (server.currentOutput == null) return;
const entry = step.entrypoints;
if (Array.isArray(entry) || entry.type !== 'content-script') return;
const js = getContentScriptJs(wxt.config, entry);
const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
const css = getContentScriptCssFiles([entry], cssMap);
server.reloadContentScript({
registration: entry.options.registration,
contentScript: mapWxtOptionsToRegisteredContentScript(
entry.options,
js,
css,
),
});
});
} else {
server.reloadExtension();
}
}
function reloadHtmlPages(
groups: EntrypointGroup[],
server: WxtDevServer,
): { reloadedNames: string[] } {
// groups might contain other files like background/content scripts, and we only care about the HTMl pages
const htmlEntries = groups.flat().filter(isHtmlEntrypoint);
htmlEntries.forEach((entry) => {
const path = getEntrypointBundlePath(entry, wxt.config.outDir, '.html');
server.reloadPage(path);
});
return {
reloadedNames: htmlEntries.map((entry) => entry.name),
};
}
function getFilenameList(names: string[]): string {
return names
.map((name) => styleText('cyan', name))
.join(styleText('dim', ', '));
}
/**
* Based on the current build output, return a list of files that are:
*
@@ -6,13 +6,13 @@ import { pnpm } from '../pnpm';
process.env.WXT_PNPM_IGNORE_WORKSPACE = 'true';
describe('PNPM Package Management Utils', () => {
describe('listDependencies', { timeout: 30e3 }, () => {
describe('listDependencies', () => {
const cwd = path.resolve(__dirname, 'fixtures/simple-pnpm-project');
beforeAll(async () => {
// PNPM needs the modules installed, or 'pnpm ls' will return a blank list.
await spawn('pnpm', ['install'], { cwd });
});
}, 30e3);
it('should list direct dependencies', async () => {
const actual = await pnpm.listDependencies({ cwd });
@@ -18,6 +18,7 @@ import {
ContentScriptEntrypoint,
Entrypoint,
OutputAsset,
TargetManifestVersion,
} from '../../../types';
import { wxt } from '../../wxt';
import { mock } from 'vitest-mock-extended';
@@ -34,6 +35,7 @@ describe('Manifest Utils', () => {
describe('generateManifest', () => {
describe('popup', () => {
type ActionType = 'browser_action' | 'page_action';
type Mv3ActionType = 'action' | 'page_action';
const popupEntrypoint = (type?: ActionType) =>
fakePopupEntrypoint({
options: {
@@ -107,29 +109,39 @@ describe('Manifest Utils', () => {
},
);
it('should allow converting action to page_action for Firefox MV3', async () => {
const popup = popupEntrypoint('page_action');
const buildOutput = fakeBuildOutput();
setFakeWxt({
config: {
manifestVersion: 3,
outDir,
browser: 'firefox',
},
});
const expected = {
default_icon: popup.options.defaultIcon,
default_title: popup.options.defaultTitle,
default_popup: 'popup.html',
};
it.each<{
inputType: ActionType | undefined;
expectedType: Mv3ActionType;
}>([
{ inputType: undefined, expectedType: 'action' },
{ inputType: 'browser_action', expectedType: 'action' },
{ inputType: 'page_action', expectedType: 'page_action' },
])(
'should use the correct action for Firefox in mv3: %j',
async ({ inputType, expectedType }) => {
const popup = popupEntrypoint(inputType);
const buildOutput = fakeBuildOutput();
setFakeWxt({
config: {
manifestVersion: 3,
outDir,
browser: 'firefox',
},
});
const expected = {
default_icon: popup.options.defaultIcon,
default_title: popup.options.defaultTitle,
default_popup: 'popup.html',
};
const { manifest: actual } = await generateManifest(
[popup],
buildOutput,
);
const { manifest: actual } = await generateManifest(
[popup],
buildOutput,
);
expect(actual.page_action).toEqual(expected);
});
expect(actual[expectedType]).toEqual(expected);
},
);
it('should include default_area for Firefox in mv3', async () => {
const popup = fakePopupEntrypoint({
@@ -187,6 +199,74 @@ describe('Manifest Utils', () => {
expect((actual.action as any).theme_icons).toEqual(themeIcons);
});
describe('default_state', () => {
it.each<{
browser: string;
manifestVersion: TargetManifestVersion;
type: 'browser_action' | 'action' | 'page_action';
shouldExist: boolean;
}>([
{
browser: 'chrome',
manifestVersion: 2,
type: 'browser_action',
shouldExist: false,
},
{
browser: 'chrome',
manifestVersion: 3,
type: 'action',
shouldExist: true,
},
{
browser: 'firefox',
manifestVersion: 2,
type: 'browser_action',
shouldExist: false,
},
{
browser: 'firefox',
manifestVersion: 3,
type: 'action',
shouldExist: true,
},
])(
'should configure default_state: $defaultState based on the $browser and mv$manifestVersion',
async ({ browser, manifestVersion, type, shouldExist }) => {
const popup = fakePopupEntrypoint({
options: {
// @ts-expect-error: Force this to be undefined when null
actionType: manifestVersion === 3 ? null : type,
defaultState: 'enabled',
},
outputDir: outDir,
skipped: false,
});
const buildOutput = fakeBuildOutput();
setFakeWxt({
config: {
browser,
manifestVersion,
outDir,
},
});
const { manifest: actual } = await generateManifest(
[popup],
buildOutput,
);
if (shouldExist) {
expect(actual[type]).toMatchObject({
default_state: 'enabled',
});
} else {
expect(actual[type].default_state).toBeUndefined();
}
},
);
});
it('should include default_area for Firefox in mv2', async () => {
const popup = fakePopupEntrypoint({
options: {
@@ -365,6 +445,28 @@ describe('Manifest Utils', () => {
expect(actual.options_ui).toEqual(expected);
});
it('should exclude open_in_tab for safari', async () => {
setFakeWxt({
config: {
manifestVersion: 3,
browser: 'safari',
outDir,
},
});
const buildOutput = fakeBuildOutput();
const { manifest: actual } = await generateManifest(
[options],
buildOutput,
);
expect(actual.options_ui).toEqual({
chrome_style: true,
page: 'options.html',
});
expect(actual.options_ui?.open_in_tab).toBeUndefined();
});
});
describe('background', () => {
@@ -1591,7 +1693,6 @@ describe('Manifest Utils', () => {
expect(actual.version).toBe('0.0.0');
expect(actual.version_name).toBeUndefined();
expect(wxt.logger.warn).toBeCalledTimes(1);
expect(wxt.logger.warn).toBeCalledWith(
expect.stringContaining('Extension version not found'),
);
@@ -2023,7 +2124,6 @@ describe('Manifest Utils', () => {
const { manifest } = await generateManifest([], buildOutput);
expect(manifest.manifest_version).toBe(expectedVersion);
expect(wxt.logger.warn).toBeCalledTimes(1);
expect(wxt.logger.warn).toBeCalledWith(
expect.stringContaining(
'`manifest.manifest_version` config was set, but ignored',
@@ -31,7 +31,6 @@ export function hashContentScriptOptions(
match_about_blank: false,
run_at: 'document_idle',
all_frames: false,
// @ts-expect-error: Untyped
match_origin_as_fallback: false,
world: 'ISOLATED',
...simplifiedOptions,
@@ -63,7 +62,6 @@ export function mapWxtOptionsToContentScript(
run_at: options.runAt,
css,
js,
// @ts-expect-error: Untyped
match_origin_as_fallback: options.matchOriginAsFallback,
world: options.world,
};
@@ -0,0 +1,166 @@
import { debounce } from 'perfect-debounce';
import { Mutex } from 'async-mutex';
import { relative } from 'node:path';
import { BuildStepOutput, EntrypointGroup, WxtDevServer } from '../../types';
import { wxt } from '../wxt';
import { detectDevChanges, findEntrypoints, rebuild } from './building';
import { getEntrypointBundlePath, isHtmlEntrypoint } from './entrypoints';
import { getContentScriptCssFiles, getContentScriptsCssMap } from './manifest';
import {
getContentScriptJs,
mapWxtOptionsToRegisteredContentScript,
} from './content-scripts';
import { isBabelSyntaxError, logBabelSyntaxError } from './syntax-errors';
import { styleText } from 'node:util';
/**
* Returns a function responsible for reloading different parts of the extension
* when a file changes.
*/
export function createFileReloader(server: WxtDevServer) {
const fileChangedMutex = new Mutex();
const changeQueue: Array<[string, string]> = [];
const cb = async (event: string, path: string) => {
changeQueue.push([event, path]);
const reloading = fileChangedMutex.runExclusive(async () => {
if (server.currentOutput == null) return;
const fileChanges = changeQueue
.splice(0, changeQueue.length)
.map(([_, file]) => file);
if (fileChanges.length === 0) return;
await wxt.reloadConfig();
const changes = detectDevChanges(fileChanges, server.currentOutput);
if (changes.type === 'no-change') return;
if (changes.type === 'full-restart') {
wxt.logger.info('Config changed, restarting server...');
server.restart();
return;
}
if (changes.type === 'browser-restart') {
wxt.logger.info('Runner config changed, restarting browser...');
server.restartBrowser();
return;
}
// Log the entrypoints that were effected
wxt.logger.info(
`Changed: ${Array.from(new Set(fileChanges))
.map((file) => styleText('dim', relative(wxt.config.root, file)))
.join(', ')}`,
);
// Rebuild entrypoints on change
const allEntrypoints = await findEntrypoints();
try {
const { output: newOutput } = await rebuild(
allEntrypoints,
// TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
changes.rebuildGroups,
changes.cachedOutput,
);
server.currentOutput = newOutput;
// Perform reloads
switch (changes.type) {
case 'extension-reload':
server.reloadExtension();
wxt.logger.success(`Reloaded extension`);
break;
case 'html-reload':
const { reloadedNames } = reloadHtmlPages(
changes.rebuildGroups,
server,
);
wxt.logger.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
break;
case 'content-script-reload':
reloadContentScripts(changes.changedSteps, server);
const rebuiltNames = changes.rebuildGroups
.flat()
.map((entry) => entry.name);
wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
break;
}
} catch {
// Catch build errors instead of crashing. Don't log error either, builder should have already logged it
}
});
await reloading.catch((error) => {
if (!isBabelSyntaxError(error)) {
throw error;
}
// Log syntax errors without crashing the server.
logBabelSyntaxError(error);
});
};
return debounce(cb, wxt.config.dev.server!.watchDebounce, {
leading: true,
trailing: false,
});
}
/**
* From the server, tell the client to reload content scripts from the provided
* build step outputs.
*/
export function reloadContentScripts(
steps: BuildStepOutput[],
server: WxtDevServer,
) {
if (wxt.config.manifestVersion === 3) {
steps.forEach((step) => {
if (server.currentOutput == null) return;
const entry = step.entrypoints;
if (Array.isArray(entry) || entry.type !== 'content-script') return;
const js = getContentScriptJs(wxt.config, entry);
const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
const css = getContentScriptCssFiles([entry], cssMap);
server.reloadContentScript({
registration: entry.options.registration,
contentScript: mapWxtOptionsToRegisteredContentScript(
entry.options,
js,
css,
),
});
});
} else {
server.reloadExtension();
}
}
function reloadHtmlPages(
groups: EntrypointGroup[],
server: WxtDevServer,
): { reloadedNames: string[] } {
// groups might contain other files like background/content scripts, and we only care about the HTMl pages
const htmlEntries = groups.flat().filter(isHtmlEntrypoint);
htmlEntries.forEach((entry) => {
const path = getEntrypointBundlePath(entry, wxt.config.outDir, '.html');
server.reloadPage(path);
});
return {
reloadedNames: htmlEntries.map((entry) => entry.name),
};
}
function getFilenameList(names: string[]): string {
return names
.map((name) => styleText('cyan', name))
.join(styleText('dim', ', '));
}
@@ -4,6 +4,7 @@ import { filesize } from 'filesize';
import { printTable } from './printTable';
import { styleText } from 'node:util';
import { TextStyle } from '../../../utils/text-style';
import { wxt } from '../../wxt';
export async function printFileList(
log: (message: string) => void,
@@ -21,9 +22,16 @@ export async function printFileList(
];
const prefix = i === files.length - 1 ? ' └─' : ' ├─';
const chunkColor = getChunkColor(file);
const stats = await lstat(file);
totalSize += stats.size;
const size = String(filesize(stats.size));
let size = '';
try {
const stats = await lstat(file);
totalSize += stats.size;
size = String(filesize(stats.size));
} catch (ex) {
wxt.logger.warn(`Could not get stats of '${file}' error: ${ex}`);
}
return [
`${styleText('gray', prefix)} ${styleText('dim', parts[0])}${styleText(chunkColor, parts[1])}`,
styleText('dim', size),
+23 -6
View File
@@ -129,6 +129,17 @@ export async function generateManifest(
);
}
if (
wxt.config.browser === 'firefox' &&
!manifest.browser_specific_settings?.gecko?.id &&
!wxt.config.suppressWarnings?.firefoxId
) {
wxt.logger.warn(
'Firefox requires extension ID for MV3 and recommends it for MV2.\n' +
'For more details, see: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_specific_settings#id',
);
}
addEntrypoints(manifest, entrypoints, buildOutput);
if (wxt.config.browser === 'firefox') {
@@ -294,6 +305,9 @@ function addEntrypoints(
options.default_icon = popup.options.defaultIcon;
if (popup.options.defaultTitle)
options.default_title = popup.options.defaultTitle;
if (popup.options.defaultState && wxt.config.manifestVersion === 3)
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Chrome
options.default_state = popup.options.defaultState;
if (popup.options.browserStyle)
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox
options.browser_style = popup.options.browserStyle;
@@ -305,10 +319,11 @@ function addEntrypoints(
options.theme_icons = popup.options.themeIcons;
const actionKey =
manifest.manifest_version === 2
wxt.config.manifestVersion === 2
? (popup.options.actionType ?? 'browser_action')
: wxt.config.browser === 'firefox'
? (popup.options.actionType ?? 'action')
: wxt.config.browser === 'firefox' &&
popup.options.actionType === 'page_action'
? 'page_action'
: 'action';
manifest[actionKey] = {
@@ -329,7 +344,9 @@ function addEntrypoints(
if (options) {
const page = getEntrypointBundlePath(options, wxt.config.outDir, '.html');
manifest.options_ui = {
open_in_tab: options.options.openInTab ?? false,
...(wxt.config.browser !== 'safari' && {
open_in_tab: options.options.openInTab ?? false,
}),
// @ts-expect-error: Not typed by @wxt-dev/browser, but supported by Firefox
browser_style:
wxt.config.browser === 'firefox'
@@ -489,7 +506,7 @@ function addDevModeCsp(manifest: Browser.runtime.Manifest): void {
const permission = `${permissionUrl}*`;
const allowedCsp = wxt.server?.origin ?? 'http://localhost:*';
if (manifest.manifest_version === 3) {
if (wxt.config.manifestVersion === 3) {
addHostPermission(manifest, permission);
} else {
addPermission(manifest, permission);
@@ -498,7 +515,7 @@ function addDevModeCsp(manifest: Browser.runtime.Manifest): void {
const extensionPagesCsp = new ContentSecurityPolicy(
// @ts-expect-error: extension_pages exists, we convert MV2 CSPs to this earlier in the process
manifest.content_security_policy?.extension_pages ??
(manifest.manifest_version === 3
(wxt.config.manifestVersion === 3
? DEFAULT_MV3_EXTENSION_PAGES_CSP
: DEFAULT_MV2_CSP),
);
@@ -8,10 +8,8 @@ import picomatch, { PicomatchOptions } from 'picomatch';
* matches any of the negative patterns, it will return `false`.
*
* @example
* ```ts
* picomatchMultiple('a.json', ['*.json', '!b.json']); // => true
* picomatchMultiple('b.json', ['*.json', '!b.json']); // => false
* ```;
*/
export function picomatchMultiple(
search: string,
+6 -5
View File
@@ -1,10 +1,10 @@
import { InlineConfig, Wxt, WxtCommand, WxtHooks, WxtModule } from '../types';
import { resolveConfig } from './resolve-config';
import { createHooks } from 'hookable';
import { createWxtPackageManager } from './package-managers';
import { createViteBuilder } from './builders/vite';
import { builtinModules } from '../builtin-modules';
import { relative } from 'path';
import { builtinModules } from '../builtin-modules';
import { InlineConfig, Wxt, WxtCommand, WxtHooks, WxtModule } from '../types';
import { createViteBuilder } from './builders/vite';
import { createWxtPackageManager } from './package-managers';
import { resolveConfig } from './resolve-config';
/**
* Global variable set once `createWxt` is called once. Since this variable is
@@ -47,6 +47,7 @@ export async function registerWxt(
inlineConfig.dev ??= {};
inlineConfig.dev.server ??= {};
inlineConfig.dev.server.port = wxt.config.dev.server.port;
inlineConfig.dev.server.strictPort = true;
}
wxt.config = await resolveConfig(inlineConfig, command);
+38 -10
View File
@@ -68,7 +68,7 @@ export interface InlineConfig {
* 'serve')
*
* @example
* {{browser}} -mv{{manifestVersion}}
* '{{browser}}-mv{{manifestVersion}}';
*
* @default <span v-pre>`"{{browser}}-mv{{manifestVersion}}{{modeSuffix}}"`</span>
*/
@@ -147,13 +147,11 @@ export interface InlineConfig {
* Suppress specific warnings during the build process.
*
* @example
* ```ts
* export default defineConfig({
* suppressWarnings: {
* firefoxDataCollection: true,
* },
* })
* ```;
* });
*/
suppressWarnings?: {
/**
@@ -161,6 +159,11 @@ export interface InlineConfig {
* https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent
*/
firefoxDataCollection?: boolean;
/**
* Suppress warnings when the Firefox extension ID is missing.
* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_specific_settings#id
*/
firefoxId?: boolean;
};
/**
* Configure browser startup. Options set here can be overridden in a
@@ -288,10 +291,10 @@ export interface InlineConfig {
*
* @example
* // Correct:
* ['@scope/package-name', 'package-name'][
* // Incorrect, don't include versions!!!
* ('@scope/package-name@1.1.3', 'package-name@^2')
* ];
* ['@scope/package-name', 'package-name'];
*
* // Incorrect, don't include versions!!!
* ['@scope/package-name@1.1.3', 'package-name@^2'];
*
* @default [ ]
*/
@@ -713,6 +716,22 @@ export interface BaseContentScriptEntrypointOptions extends BaseScriptEntrypoint
* @default 'manifest'
*/
registration?: PerBrowserOption<'manifest' | 'runtime'>;
/**
* Do not send the `wxt:content-script-started` message via
* `window.postMessage`.
*
* This has been replaced with custom events. The `postMessage` call is kept
* for backwards compatibility. For some websites the `postMessage` call is
* undesirable, such as those with poorly written message event listeners.
*
* Setting this to `true` opts into the behavior that will become the default
* in a future version of WXT, where the `postMessage` call is removed
* entirely.
*
* See https://github.com/wxt-dev/wxt/pull/1938 and
* https://github.com/wxt-dev/wxt/pull/2035 for a detailed discussion.
*/
noScriptStartedPostMessage?: boolean;
}
export interface MainWorldContentScriptEntrypointOptions extends BaseContentScriptEntrypointOptions {
@@ -760,6 +779,12 @@ export interface PopupEntrypointOptions extends BaseEntrypointOptions {
mv2Key?: PerBrowserOption<'browser_action' | 'page_action'>;
defaultIcon?: Record<string, string>;
defaultTitle?: PerBrowserOption<string>;
/**
* Chrome only. Controls the initial enabled/disabled state of the action.
*
* @see https://developer.chrome.com/docs/extensions/reference/api/action#enabled_state
*/
defaultState?: PerBrowserOption<'enabled' | 'disabled'>;
browserStyle?: PerBrowserOption<boolean>;
/**
* Firefox only. Defines the part of the browser in which the button is
@@ -1060,7 +1085,7 @@ export type UserManifest = {
};
};
permissions?: (
| Browser.runtime.ManifestPermissions
| Browser.runtime.ManifestPermission
| (string & Record<never, never>)
)[];
web_accessible_resources?:
@@ -1510,7 +1535,10 @@ export interface ResolvedConfig {
alias: Record<string, string>;
experimental: {};
/** List of warning identifiers to suppress during the build process. */
suppressWarnings: { firefoxDataCollection?: boolean };
suppressWarnings: {
firefoxDataCollection?: boolean;
firefoxId?: boolean;
};
dev: {
/** Only defined during dev command */
server?: {
@@ -84,6 +84,22 @@ describe('Content Script Context', () => {
expect(ctx.isValid).toBe(true);
});
describe('noScriptStartedPostMessage', () => {
it('should send window.postMessage by default', async () => {
const postMessageSpy = vi.spyOn(window, 'postMessage');
new ContentScriptContext('test');
expect(postMessageSpy).toHaveBeenCalledOnce();
postMessageSpy.mockRestore();
});
it('should not send window.postMessage when noScriptStartedPostMessage is true', async () => {
const postMessageSpy = vi.spyOn(window, 'postMessage');
new ContentScriptContext('test', { noScriptStartedPostMessage: true });
expect(postMessageSpy).not.toHaveBeenCalled();
postMessageSpy.mockRestore();
});
});
describe('addEventListener', () => {
const context = new ContentScriptContext('test');
it('should infer types correctly for the window target', () => {
@@ -258,16 +258,18 @@ export class ContentScriptContext implements AbortController {
}),
);
// Send message using `window.postMessage` for backwards compatibility to invalidate old versions before WXT changed to `document.dispatchEvent`
// TODO: Remove this once WXT version using `document.dispatchEvent` has been released for a while
window.postMessage(
{
type: ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE,
contentScriptName: this.contentScriptName,
messageId: this.id,
},
'*',
);
if (!this.options?.noScriptStartedPostMessage) {
// Send message using `window.postMessage` for backwards compatibility to invalidate old versions before WXT changed to `document.dispatchEvent`
// TODO: Remove this once WXT version using `document.dispatchEvent` has been released for a while
window.postMessage(
{
type: ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE,
contentScriptName: this.contentScriptName,
messageId: this.id,
},
'*',
);
}
}
verifyScriptStartedEvent(event: CustomEvent) {
-7
View File
@@ -1,7 +0,0 @@
Bun outputs packed tarballs relative to the workspace root when using `--filename`. So we need to use an absolute patht make sure the file is output where pkg-pr-new expects it to be - in the package's directory.
`p3` is equal to: '/home/runner/work/wxt/wxt/packages/analytics/'
This has been fixed in <https://github.com/stackblitz-labs/pkg.pr.new/pull/483>, but is not released as of `0.66.0`.
Once v0.67.0 is released, we can revert back to using bunx, delete this patch, and remove the local dependency in `<root>/package.json`.
-47
View File
@@ -1,47 +0,0 @@
diff --git a/node_modules/pkg-pr-new/.bun-tag-c719f450f9f5410d b/.bun-tag-c719f450f9f5410d
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/dist/index.js b/dist/index.js
index e2626a009efe1ee3cfa6c96fe712341d3501ba1c..20755fe90b3c8807b28e63e9ebcfb244aea07be9 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -3743,7 +3743,7 @@ async function resolveTarball(pm, p3, pJson) {
if (pm === "yarn") {
cmd += ` --filename ${filename}`;
} else if (pm === "bun") {
- cmd = `bun pm pack --quiet --filename ${filename}`;
+ cmd = `bun pm pack --quiet --filename ${p3}${filename}`;
}
const { stdout: stdout2 } = await ezSpawn.async(cmd, {
stdio: "overlapped",
@@ -3753,18 +3753,18 @@ async function resolveTarball(pm, p3, pJson) {
if (pm !== "yarn" && pm !== "bun") {
filename = lines[lines.length - 1].trim();
}
- if (pm === "bun") {
- const tgzFiles = fsSync.readdirSync(p3).filter((file) => file.endsWith(".tgz"));
- const bunFilename = stdout2.trim();
- if (bunFilename) {
- filename = bunFilename;
- }
- console.warn(`[bun pack] stdout:
-${stdout2}`);
- console.warn(
- `[bun pack] expected filename: ${filename}; tgz files: ${tgzFiles.join(", ") || "(none)"}`
- );
- }
+// if (pm === "bun") {
+// const tgzFiles = fsSync.readdirSync(p3).filter((file) => file.endsWith(".tgz"));
+// const bunFilename = stdout2.trim();
+// if (bunFilename) {
+// filename = bunFilename;
+// }
+// console.warn(`[bun pack] stdout:
+// ${stdout2}`);
+// console.warn(
+// `[bun pack] expected filename: ${filename}; tgz files: ${tgzFiles.join(", ") || "(none)"}`
+// );
+// }
const shasum = createHash("sha1").update(await fs.readFile(path.resolve(p3, filename))).digest("hex");
return { filename, shasum };
}
+1 -1
View File
@@ -23,6 +23,6 @@
"@types/react-dom": "^19.2.3",
"@wxt-dev/module-react": "^1.1.5",
"typescript": "^5.9.3",
"wxt": "^0.20.23"
"wxt": "^0.20.27"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"devDependencies": {
"@wxt-dev/module-solid": "^1.1.4",
"typescript": "^5.9.3",
"wxt": "^0.20.23"
"wxt": "^0.20.27"
}
}
+1 -1
View File
@@ -21,6 +21,6 @@
"svelte-check": "^4.4.4",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"wxt": "^0.20.23"
"wxt": "^0.20.27"
}
}
+1 -1
View File
@@ -16,6 +16,6 @@
},
"devDependencies": {
"typescript": "^5.9.3",
"wxt": "^0.20.23"
"wxt": "^0.20.27"
}
}
+1 -1
View File
@@ -21,6 +21,6 @@
"@wxt-dev/module-vue": "^1.0.3",
"typescript": "^5.9.3",
"vue-tsc": "^3.2.5",
"wxt": "^0.20.23"
"wxt": "^0.20.27"
}
}
+1
View File
@@ -10,6 +10,7 @@
/* Type Checking */
"strict": true,
"lib": ["DOM", "WebWorker", "ESNext"],
"types": ["bun"],
/* Completeness */
"skipLibCheck": true