Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb851b1738 | |||
| 82825a5b35 | |||
| a63f2d2a33 | |||
| d3983a603f | |||
| 9304319f55 | |||
| e778449377 | |||
| 3ecafbe0f3 | |||
| 9ab345b6dc | |||
| ade1a6b670 | |||
| af840d7169 | |||
| f2ced3f0e4 | |||
| eb2e151520 | |||
| b235c8591e | |||
| 4c11f00176 | |||
| f75d1e1afc | |||
| 4f419da1dc | |||
| d9c25ac4ce | |||
| 8dfedf2a5a | |||
| 19f94e1e6a | |||
| fc269ed78d | |||
| cfc633202b | |||
| 6f1790deef | |||
| 331ce14170 | |||
| a4c6cd1cd0 | |||
| cb0e336812 | |||
| ba7c9cef60 | |||
| 70b30ae431 | |||
| 12c267a5d1 | |||
| 38625a8cd3 | |||
| 700cd8c3e8 | |||
| c272fbe0ae | |||
| f40005da36 | |||
| 34afac0165 | |||
| 7cbe9a477b | |||
| 081d472176 | |||
| d7f8ebec39 | |||
| bde65e2066 | |||
| 4896cd208e | |||
| 42a5b3cd67 | |||
| d60fd2516f | |||
| 6b2c27cf45 | |||
| 1a3f4f64e5 | |||
| 492cbb4644 | |||
| c8e93b15db | |||
| e3f4fe3e13 | |||
| 8cb973cf3c | |||
| 22d1a72752 | |||
| 8f90331229 | |||
| 9a638449d5 | |||
| 9ce9b714f6 | |||
| 22b7aba035 | |||
| 4cbcf282f7 | |||
| d8b1ddfb59 | |||
| 7905cb379b | |||
| 1b2463d52e | |||
| b567f9957a | |||
| c2fb8c786d | |||
| 4320d8e1fa | |||
| 344d14dafc | |||
| 3cb894c4e5 | |||
| dbbe14ddd6 | |||
| 9ee07f431a | |||
| d517e9a0b9 | |||
| d9adbb6f09 | |||
| 6b77277cea | |||
| bad24392fc | |||
| f24e62129b | |||
| f38cb855ea | |||
| 891a0d50a6 | |||
| cfb384e618 |
+1
-1
@@ -9,4 +9,4 @@
|
||||
/packages/unocss/ @Timeraa
|
||||
|
||||
# Other Directories
|
||||
/docs @aklinker1 @PatrykKuniczak
|
||||
/docs @aklinker1 @PatrykKuniczak @nishu-murmu
|
||||
|
||||
@@ -62,14 +62,18 @@ jobs:
|
||||
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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
if: github.repository_owner == 'wxt-dev'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
if: ${{ github.repository == 'wxt-dev/wxt' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
# Only fetch the config file from the repository
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- validate
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ssh-key: ${{ secrets.DEPLOY_KEY }} # https://github.com/sbellone/release-workflow-example
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
id-token: write # OIDC for NPM publishing
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
- name: Commit Changes
|
||||
id: commit
|
||||
uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
|
||||
uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
@@ -68,15 +68,15 @@ jobs:
|
||||
coverage: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@91ab88e2619ed1f46221f0ba42d1492c02baf788 # v6.0.6
|
||||
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||
with:
|
||||
version: 10.33.0
|
||||
version: latest
|
||||
|
||||
- name: Run Tests
|
||||
if: ${{ ! matrix.coverage }}
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
|
||||
- name: Upload Coverage
|
||||
if: matrix.coverage
|
||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
- vue
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
@@ -17,27 +17,20 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.ref_name }}
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@91ab88e2619ed1f46221f0ba42d1492c02baf788 # v6.0.6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
cache: pnpm
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16
|
||||
with:
|
||||
go-version: '1.25.1'
|
||||
|
||||
# This prevents pnpm dlx from downloading WXT in the video
|
||||
- name: Pre-install WXT
|
||||
run: |
|
||||
pnpm store add wxt@latest
|
||||
pnpm dlx wxt@latest --version
|
||||
# This prevents bunx from downloading WXT in the video, and it uses the checked-out version
|
||||
- name: Build WXT
|
||||
working-directory: packages/wxt
|
||||
run: bun run build
|
||||
|
||||
- name: Record VHS
|
||||
run: |
|
||||
@@ -46,7 +39,7 @@ jobs:
|
||||
vhs docs/tapes/init-demo.tape
|
||||
|
||||
- name: Save recorded GIF
|
||||
uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
|
||||
uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
@@ -6,25 +6,25 @@
|
||||
"devDependencies": {
|
||||
"@aklinker1/buildc": "catalog:",
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@commitlint/config-conventional": "^20.4.3",
|
||||
"@commitlint/types": "^20.4.3",
|
||||
"@commitlint/config-conventional": "^21.2.0",
|
||||
"@commitlint/types": "^21.2.0",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@vitest/coverage-v8": "^4.1.5",
|
||||
"@vitest/coverage-v8": "^4.1.6",
|
||||
"changelogen": "^0.6.2",
|
||||
"consola": "^3.4.2",
|
||||
"cspell": "^9.7.0",
|
||||
"feed": "^5.2.0",
|
||||
"feed": "^5.2.1",
|
||||
"markdown-it-footnote": "^4.0.0",
|
||||
"markdownlint-cli": "^0.48.0",
|
||||
"markdownlint-cli": "^0.49.0",
|
||||
"nano-spawn": "catalog:",
|
||||
"nano-staged": "^1.0.2",
|
||||
"p-map": "^7.0.4",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-jsdoc": "^1.8.0",
|
||||
"semver": "^7.7.4",
|
||||
"semver": "^7.8.0",
|
||||
"simple-git-hooks": "^2.13.1",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyglobby": "^0.2.16",
|
||||
"tsdown": "catalog:",
|
||||
"tsx": "4.21.0",
|
||||
"typedoc": "^0.25.4",
|
||||
@@ -43,7 +43,7 @@
|
||||
},
|
||||
"packages/analytics": {
|
||||
"name": "@wxt-dev/analytics",
|
||||
"version": "0.5.4",
|
||||
"version": "0.5.5",
|
||||
"dependencies": {
|
||||
"@wxt-dev/browser": "workspace:^",
|
||||
"@wxt-dev/is-background": "workspace:^",
|
||||
@@ -83,13 +83,13 @@
|
||||
},
|
||||
"packages/browser": {
|
||||
"name": "@wxt-dev/browser",
|
||||
"version": "0.1.42",
|
||||
"version": "0.2.0",
|
||||
"dependencies": {
|
||||
"@types/filesystem": "*",
|
||||
"@types/har-format": "*",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chrome": "0.1.42",
|
||||
"@types/chrome": "0.2.0",
|
||||
"@types/node": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
@@ -103,13 +103,14 @@
|
||||
"@wxt-dev/browser": "workspace:^",
|
||||
"chokidar": "^5.0.0",
|
||||
"confbox": "^0.2.4",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyglobby": "^0.2.16",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/buildc": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"tinybench": "^6.0.1",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
@@ -251,7 +252,7 @@
|
||||
"version": "1.0.1",
|
||||
"dependencies": {
|
||||
"defu": "^6.1.4",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyglobby": "^0.2.16",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/buildc": "catalog:",
|
||||
@@ -286,7 +287,7 @@
|
||||
},
|
||||
"packages/wxt": {
|
||||
"name": "wxt",
|
||||
"version": "0.20.26",
|
||||
"version": "0.20.27",
|
||||
"bin": {
|
||||
"wxt": "./bin/wxt.mjs",
|
||||
"wxt-publish-extension": "./bin/wxt-publish-extension.mjs",
|
||||
@@ -300,7 +301,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",
|
||||
@@ -308,7 +309,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",
|
||||
@@ -324,12 +325,11 @@
|
||||
"nypm": "^0.6.5",
|
||||
"ohash": "^2.0.11",
|
||||
"open": "^11.0.0",
|
||||
"perfect-debounce": "^2.1.0",
|
||||
"picomatch": "^4.0.3",
|
||||
"prompts": "^2.4.2",
|
||||
"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",
|
||||
@@ -498,9 +498,11 @@
|
||||
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="],
|
||||
|
||||
"@commitlint/config-conventional": ["@commitlint/config-conventional@20.5.0", "", { "dependencies": { "@commitlint/types": "^20.5.0", "conventional-changelog-conventionalcommits": "^9.2.0" } }, "sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA=="],
|
||||
"@commitlint/config-conventional": ["@commitlint/config-conventional@21.2.0", "", { "dependencies": { "@commitlint/types": "^21.2.0", "conventional-changelog-conventionalcommits": "^10.0.0" } }, "sha512-Qf8WRDVcyVd14if6VTWenebxFbKnVnbzPUJjlzjkyJGeHK2xCGd63Dr1XZzj0plXKQb9P0BfOxoc1HVeCo2BWQ=="],
|
||||
|
||||
"@commitlint/types": ["@commitlint/types@20.5.0", "", { "dependencies": { "conventional-commits-parser": "^6.3.0", "picocolors": "^1.1.1" } }, "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA=="],
|
||||
"@commitlint/types": ["@commitlint/types@21.2.0", "", { "dependencies": { "conventional-commits-parser": "^7.0.0", "picocolors": "^1.1.1" } }, "sha512-7zVFCDB2reMvJH5dmbKnOQPjZEvjdJTH8jc0U/PIPU1r3/+vf5pD1HlfitV2MWsWXrvu7u39iY1lyLUPOaN0Gw=="],
|
||||
|
||||
"@conventional-changelog/template": ["@conventional-changelog/template@1.2.0", "", {}, "sha512-12qHxvlKjHmP0PQ+17EREgC7lWyLwbph1RKcZQZ7k7ZWGmrxfxC9gadHGfvzr0g0u8BhiBGg3tks93txodlyRQ=="],
|
||||
|
||||
"@cspell/cspell-bundled-dicts": ["@cspell/cspell-bundled-dicts@9.8.0", "", { "dependencies": { "@cspell/dict-ada": "^4.1.1", "@cspell/dict-al": "^1.1.1", "@cspell/dict-aws": "^4.0.17", "@cspell/dict-bash": "^4.2.2", "@cspell/dict-companies": "^3.2.11", "@cspell/dict-cpp": "^7.0.2", "@cspell/dict-cryptocurrencies": "^5.0.5", "@cspell/dict-csharp": "^4.0.8", "@cspell/dict-css": "^4.1.1", "@cspell/dict-dart": "^2.3.2", "@cspell/dict-data-science": "^2.0.13", "@cspell/dict-django": "^4.1.6", "@cspell/dict-docker": "^1.1.17", "@cspell/dict-dotnet": "^5.0.13", "@cspell/dict-elixir": "^4.0.8", "@cspell/dict-en-common-misspellings": "^2.1.12", "@cspell/dict-en-gb-mit": "^3.1.22", "@cspell/dict-en_us": "^4.4.33", "@cspell/dict-filetypes": "^3.0.18", "@cspell/dict-flutter": "^1.1.1", "@cspell/dict-fonts": "^4.0.6", "@cspell/dict-fsharp": "^1.1.1", "@cspell/dict-fullstack": "^3.2.9", "@cspell/dict-gaming-terms": "^1.1.2", "@cspell/dict-git": "^3.1.0", "@cspell/dict-golang": "^6.0.26", "@cspell/dict-google": "^1.0.9", "@cspell/dict-haskell": "^4.0.6", "@cspell/dict-html": "^4.0.15", "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-java": "^5.0.12", "@cspell/dict-julia": "^1.1.1", "@cspell/dict-k8s": "^1.0.12", "@cspell/dict-kotlin": "^1.1.1", "@cspell/dict-latex": "^5.1.0", "@cspell/dict-lorem-ipsum": "^4.0.5", "@cspell/dict-lua": "^4.0.8", "@cspell/dict-makefile": "^1.0.5", "@cspell/dict-markdown": "^2.0.16", "@cspell/dict-monkeyc": "^1.0.12", "@cspell/dict-node": "^5.0.9", "@cspell/dict-npm": "^5.2.38", "@cspell/dict-php": "^4.1.1", "@cspell/dict-powershell": "^5.0.15", "@cspell/dict-public-licenses": "^2.0.16", "@cspell/dict-python": "^4.2.26", "@cspell/dict-r": "^2.1.1", "@cspell/dict-ruby": "^5.1.1", "@cspell/dict-rust": "^4.1.2", "@cspell/dict-scala": "^5.0.9", "@cspell/dict-shell": "^1.1.2", "@cspell/dict-software-terms": "^5.2.2", "@cspell/dict-sql": "^2.2.1", "@cspell/dict-svelte": "^1.0.7", "@cspell/dict-swift": "^2.0.6", "@cspell/dict-terraform": "^1.1.3", "@cspell/dict-typescript": "^3.2.3", "@cspell/dict-vue": "^3.0.5", "@cspell/dict-zig": "^1.0.0" } }, "sha512-MpXFpVyBPfJQ1YuVotljqUaGf6lWuf+fuWBBgs0PHFYTSjRPWuIxviAaCDnup/CJLLH60xQL4IlcQe4TOjzljw=="],
|
||||
|
||||
@@ -1036,7 +1038,7 @@
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
"@simple-libs/stream-utils": ["@simple-libs/stream-utils@1.2.0", "", {}, "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA=="],
|
||||
"@simple-libs/stream-utils": ["@simple-libs/stream-utils@2.0.0", "", {}, "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
@@ -1060,7 +1062,7 @@
|
||||
|
||||
"@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
|
||||
|
||||
"@types/chrome": ["@types/chrome@0.1.42", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" } }, "sha512-tdT2roFqGecZZDjA9fUEAINb2STxSPifHMDvY6EfRjNRCjdrs/0FwKt5RCIA9MKMd1arAYZZL3nwEkp6ZLZu2w=="],
|
||||
"@types/chrome": ["@types/chrome@0.2.0", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" } }, "sha512-K/i7EKEVSpmyXXjE0ev5oASWBYMY6yM8LFSutG2PkM1DmxJUggZsoaTc0W1RsVwDDKrsZEjpHFAbqqxlRxGsSg=="],
|
||||
|
||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||
|
||||
@@ -1176,7 +1178,7 @@
|
||||
|
||||
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.4", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.2" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "vue": "^3.2.25" } }, "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ=="],
|
||||
|
||||
"@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.5", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.5", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.5", "vitest": "4.1.5" }, "optionalPeers": ["@vitest/browser"] }, "sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A=="],
|
||||
"@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.6", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.6", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.6", "vitest": "4.1.6" }, "optionalPeers": ["@vitest/browser"] }, "sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ=="],
|
||||
|
||||
"@vitest/expect": ["@vitest/expect@4.1.5", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.5", "@vitest/utils": "4.1.5", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw=="],
|
||||
|
||||
@@ -1190,7 +1192,7 @@
|
||||
|
||||
"@vitest/spy": ["@vitest/spy@4.1.5", "", {}, "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ=="],
|
||||
|
||||
"@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="],
|
||||
"@vitest/utils": ["@vitest/utils@4.1.6", "", { "dependencies": { "@vitest/pretty-format": "4.1.6", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ=="],
|
||||
|
||||
"@vue/compiler-core": ["@vue/compiler-core@3.5.34", "", { "dependencies": { "@babel/parser": "^7.29.3", "@vue/shared": "3.5.34", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw=="],
|
||||
|
||||
@@ -1284,8 +1286,6 @@
|
||||
|
||||
"array-differ": ["array-differ@4.0.0", "", {}, "sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw=="],
|
||||
|
||||
"array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="],
|
||||
|
||||
"array-timsort": ["array-timsort@1.0.3", "", {}, "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="],
|
||||
|
||||
"array-union": ["array-union@3.0.1", "", {}, "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw=="],
|
||||
@@ -1342,7 +1342,7 @@
|
||||
|
||||
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
||||
|
||||
"c12": ["c12@3.3.3", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q=="],
|
||||
"c12": ["c12@3.3.4", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.4", "defu": "^6.1.6", "dotenv": "^17.3.1", "exsolve": "^1.0.8", "giget": "^3.2.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.1.0", "pkg-types": "^2.3.0", "rc9": "^3.0.1" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA=="],
|
||||
|
||||
"cac": ["cac@7.0.0", "", {}, "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ=="],
|
||||
|
||||
@@ -1404,8 +1404,6 @@
|
||||
|
||||
"comment-parser": ["comment-parser@1.4.6", "", {}, "sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg=="],
|
||||
|
||||
"compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"concat-stream": ["concat-stream@1.6.2", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw=="],
|
||||
@@ -1418,9 +1416,9 @@
|
||||
|
||||
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
||||
|
||||
"conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@9.3.1", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw=="],
|
||||
"conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@10.2.0", "", { "dependencies": { "@conventional-changelog/template": "^1.2.0" } }, "sha512-UtlM9GqolY7OmlQh5L/UEVoKsTUpTgUVy1PU8JN5gl5Ydaejb7WRklGliG1SKPxxj7hzA173eG3Kt5fYWE2pmg=="],
|
||||
|
||||
"conventional-commits-parser": ["conventional-commits-parser@6.4.0", "", { "dependencies": { "@simple-libs/stream-utils": "^1.2.0", "meow": "^13.0.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" } }, "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw=="],
|
||||
"conventional-commits-parser": ["conventional-commits-parser@7.0.0", "", { "dependencies": { "@simple-libs/stream-utils": "^2.0.0", "meow": "^14.0.0" }, "bin": { "conventional-commits-parser": "./dist/cli/index.js" } }, "sha512-dZe/p+FgGQLNJFqaCgNdl8j6a7gI8xuaN30Wy5g7nvyK3jqOpNUEUZ3pGJ5D68h89uRh038FtkeOk/bnGmYkmg=="],
|
||||
|
||||
"convert-gitmoji": ["convert-gitmoji@0.1.5", "", {}, "sha512-4wqOafJdk2tqZC++cjcbGcaJ13BZ3kwldf06PTiAQRAB76Z1KJwZNL1SaRZMi2w1FM9RYTgZ6QErS8NUl/GBmQ=="],
|
||||
|
||||
@@ -1478,7 +1476,7 @@
|
||||
|
||||
"define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
|
||||
|
||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
"defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="],
|
||||
|
||||
"dependency-graph": ["dependency-graph@1.0.0", "", {}, "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg=="],
|
||||
|
||||
@@ -1500,9 +1498,9 @@
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="],
|
||||
"dot-prop": ["dot-prop@9.0.0", "", { "dependencies": { "type-fest": "^4.18.2" } }, "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ=="],
|
||||
|
||||
"dotenv": ["dotenv@17.2.4", "", {}, "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw=="],
|
||||
"dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
|
||||
|
||||
"dotenv-expand": ["dotenv-expand@12.0.3", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA=="],
|
||||
|
||||
@@ -1598,11 +1596,11 @@
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"feed": ["feed@5.2.0", "", { "dependencies": { "xml-js": "^1.6.11" } }, "sha512-hgH6CCb+7+0c8PBlakI2KubG6R+Rb1MhpNcdvqUXZTBwBHf32piwY255diAkAmkGZ6AWlywOU88AkOgP9q8Rdw=="],
|
||||
"feed": ["feed@5.2.1", "", { "dependencies": { "xml-js": "^1.6.11" } }, "sha512-jTynzYPWs9ALjro0GW8j7sv9y7cJBeOdD4Y88kVqYy/eyusIX3g+499JiTDIlD9Ge/unebx57T4Uzo6vpYvMtA=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"filesize": ["filesize@11.0.15", "", {}, "sha512-30TpbYxQxCpi4XdVjkwXYQ37CzZltV38+P7MYroQ+4NK/Dmx9mxixFNrolzcmEIBsjT/uowC9T7kiy2+C12r1A=="],
|
||||
"filesize": ["filesize@11.0.17", "", {}, "sha512-oHLTvMLw6imZUl1se/RBQrFlyy50nXce4sU7yGR6Qc0JgCwqnfiFsAnEwotdGmfKLD7SArGUk2/5STU0k8LOBQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
@@ -1634,7 +1632,7 @@
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
"get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
|
||||
|
||||
"get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="],
|
||||
|
||||
@@ -1642,7 +1640,7 @@
|
||||
|
||||
"get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="],
|
||||
|
||||
"giget": ["giget@3.1.2", "", { "bin": { "giget": "dist/cli.mjs" } }, "sha512-T2qUpKBHeUTwHcIhydgnJzhL0Hj785ms+JkxaaWQH9SDM/llXeewnOkfJcFShAHjWI+26hOChwUfCoupaXLm8g=="],
|
||||
"giget": ["giget@3.2.0", "", { "bin": { "giget": "dist/cli.mjs" } }, "sha512-GvHTWcykIR/fP8cj8dMpuMMkvaeJfPvYnhq0oW+chSeIr+ldX21ifU2Ms6KBoyKZQZmVaUAAhQ2EZ68KJF8a7A=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
@@ -1732,8 +1730,6 @@
|
||||
|
||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
|
||||
"is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="],
|
||||
|
||||
"is-path-inside": ["is-path-inside@4.0.0", "", {}, "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="],
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
@@ -1770,7 +1766,7 @@
|
||||
|
||||
"js-tokens": ["js-tokens@10.0.0", "", {}, "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
"js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
@@ -1820,7 +1816,7 @@
|
||||
|
||||
"linkedom": ["linkedom@0.18.12", "", { "dependencies": { "css-select": "^5.1.0", "cssom": "^0.5.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "uhyphen": "^0.2.0" }, "peerDependencies": { "canvas": ">= 2" }, "optionalPeers": ["canvas"] }, "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q=="],
|
||||
|
||||
"linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
|
||||
"linkify-it": ["linkify-it@5.0.1", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg=="],
|
||||
|
||||
"listr2": ["listr2@10.1.0", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^7.0.2", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-/6t2KgDYIcCjhELwvrRxi1gaJ4xCGLTjNvh6mSjYenBkrZxggek8EwCbwBU33GMUCpyyrOzz2TzylrO5mTiI1w=="],
|
||||
|
||||
@@ -1868,15 +1864,15 @@
|
||||
|
||||
"mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="],
|
||||
|
||||
"markdown-it": ["markdown-it@14.1.1", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="],
|
||||
"markdown-it": ["markdown-it@14.2.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.1", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ=="],
|
||||
|
||||
"markdown-it-footnote": ["markdown-it-footnote@4.0.0", "", {}, "sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ=="],
|
||||
|
||||
"markdown-title": ["markdown-title@1.0.2", "", {}, "sha512-MqIQVVkz+uGEHi3TsHx/czcxxCbRIL7sv5K5DnYw/tI+apY54IbPefV/cmgxp6LoJSEx/TqcHdLs/298afG5QQ=="],
|
||||
|
||||
"markdownlint": ["markdownlint@0.40.0", "", { "dependencies": { "micromark": "4.0.2", "micromark-core-commonmark": "2.0.3", "micromark-extension-directive": "4.0.0", "micromark-extension-gfm-autolink-literal": "2.1.0", "micromark-extension-gfm-footnote": "2.1.0", "micromark-extension-gfm-table": "2.1.1", "micromark-extension-math": "3.1.0", "micromark-util-types": "2.0.2", "string-width": "8.1.0" } }, "sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg=="],
|
||||
"markdownlint": ["markdownlint@0.41.0", "", { "dependencies": { "micromark": "4.0.2", "micromark-core-commonmark": "2.0.3", "micromark-extension-directive": "4.0.0", "micromark-extension-gfm-autolink-literal": "2.1.0", "micromark-extension-gfm-footnote": "2.1.0", "micromark-extension-gfm-table": "2.1.1", "micromark-extension-math": "3.1.0", "micromark-util-types": "2.0.2", "string-width": "8.2.1" } }, "sha512-xMUI3ChBuRuxuLF4ENvCZyS8z/+Jly1coUcZwErKLIB3sDj7ojpaTBa1e9YVPhSN4jGEIjYGQCldbTJS/hqS+A=="],
|
||||
|
||||
"markdownlint-cli": ["markdownlint-cli@0.48.0", "", { "dependencies": { "commander": "~14.0.3", "deep-extend": "~0.6.0", "ignore": "~7.0.5", "js-yaml": "~4.1.1", "jsonc-parser": "~3.3.1", "jsonpointer": "~5.0.1", "markdown-it": "~14.1.1", "markdownlint": "~0.40.0", "minimatch": "~10.2.4", "run-con": "~1.3.2", "smol-toml": "~1.6.0", "tinyglobby": "~0.2.15" }, "bin": { "markdownlint": "markdownlint.js" } }, "sha512-NkZQNu2E0Q5qLEEHwWj674eYISTLD4jMHkBzDobujXd1kv+yCxi8jOaD/rZoQNW1FBBMMGQpuW5So8B51N/e0A=="],
|
||||
"markdownlint-cli": ["markdownlint-cli@0.49.0", "", { "dependencies": { "commander": "~15.0.0", "deep-extend": "~0.6.0", "ignore": "~7.0.5", "js-yaml": "~4.2.0", "jsonc-parser": "~3.3.1", "jsonpointer": "~5.0.1", "markdown-it": "~14.2.0", "markdownlint": "~0.41.0", "minimatch": "~10.2.5", "run-con": "~1.3.2", "smol-toml": "~1.6.1", "tinyglobby": "~0.2.17" }, "bin": { "markdownlint": "markdownlint.js" } }, "sha512-vS5tWq5W91Gg33LD4pyAaXPclnz/sRvo6/RGOyDQjQ3eds2DkK6H4szUuE0M9TiRB/u/VBx1gtd9Ktrtx5WlSA=="],
|
||||
|
||||
"marked": ["marked@4.3.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A=="],
|
||||
|
||||
@@ -1898,7 +1894,7 @@
|
||||
|
||||
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
|
||||
|
||||
"meow": ["meow@13.2.0", "", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="],
|
||||
"meow": ["meow@14.1.0", "", {}, "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw=="],
|
||||
|
||||
"merge-anything": ["merge-anything@5.1.7", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ=="],
|
||||
|
||||
@@ -2130,7 +2126,7 @@
|
||||
|
||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
||||
|
||||
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
|
||||
"rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="],
|
||||
|
||||
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
||||
|
||||
@@ -2208,7 +2204,7 @@
|
||||
|
||||
"section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
|
||||
|
||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
"semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
|
||||
|
||||
"seroval": ["seroval@1.5.0", "", {}, "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw=="],
|
||||
|
||||
@@ -2272,7 +2268,7 @@
|
||||
|
||||
"std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="],
|
||||
|
||||
"string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="],
|
||||
"string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
@@ -2304,11 +2300,11 @@
|
||||
|
||||
"through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
|
||||
|
||||
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||
"tinybench": ["tinybench@6.0.1", "", {}, "sha512-cMdWsxmysdg8mNWf1pujiWl3TW0cU6m8QuNw55QlnP3I6N96Grb0wnu5N0syHIu3LbiVZCNqlfWzWDq84HZphA=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
|
||||
"tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="],
|
||||
|
||||
@@ -2504,6 +2500,8 @@
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"@1natsu/wait-element/defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"@aklinker1/buildc/cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
|
||||
"@aklinker1/buildc/ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="],
|
||||
@@ -2548,18 +2546,22 @@
|
||||
|
||||
"@unocss/cli/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"@unocss/cli/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
|
||||
"@unocss/vite/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"@unocss/vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
|
||||
"@vitejs/plugin-vue/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.2", "", {}, "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw=="],
|
||||
|
||||
"@vitest/expect/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="],
|
||||
|
||||
"@vitest/runner/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="],
|
||||
|
||||
"@vitest/runner/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"@vitest/snapshot/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="],
|
||||
|
||||
"@vitest/snapshot/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@4.1.6", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw=="],
|
||||
|
||||
"@vue/compiler-core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="],
|
||||
|
||||
"@vue/compiler-core/@vue/shared": ["@vue/shared@3.5.34", "", {}, "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA=="],
|
||||
@@ -2598,9 +2600,9 @@
|
||||
|
||||
"@vueuse/shared/vue": ["vue@3.5.28", "", { "dependencies": { "@vue/compiler-dom": "3.5.28", "@vue/compiler-sfc": "3.5.28", "@vue/runtime-dom": "3.5.28", "@vue/server-renderer": "3.5.28", "@vue/shared": "3.5.28" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg=="],
|
||||
|
||||
"@wxt-dev/i18n/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
"@wxt-dev/i18n/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
||||
|
||||
"@wxt-dev/unocss/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
"@wxt-dev/unocss/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
||||
|
||||
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
@@ -2614,14 +2616,16 @@
|
||||
|
||||
"boxen/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
"c12/giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
|
||||
|
||||
"c12/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"changelogen/c12": ["c12@3.3.3", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q=="],
|
||||
|
||||
"changelogen/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
||||
|
||||
"changelogen/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"changelogen/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"changelogen/std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
|
||||
|
||||
"chrome-launcher/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
||||
@@ -2636,14 +2640,18 @@
|
||||
|
||||
"config-chain/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"configstore/dot-prop": ["dot-prop@9.0.0", "", { "dependencies": { "type-fest": "^4.18.2" } }, "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ=="],
|
||||
|
||||
"copy-anything/is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
|
||||
|
||||
"cspell/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"cspell/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"cspell-config-lib/yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"dot-prop/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
"dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||
|
||||
"eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
@@ -2672,12 +2680,20 @@
|
||||
|
||||
"istanbul-reports/html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="],
|
||||
|
||||
"jsonwebtoken/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
||||
|
||||
"local-pkg/quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
||||
|
||||
"make-dir/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"markdownlint-cli/commander": ["commander@15.0.0", "", {}, "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg=="],
|
||||
|
||||
"markdownlint-cli/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
||||
|
||||
"mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
@@ -2690,12 +2706,16 @@
|
||||
|
||||
"node-notifier/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
||||
|
||||
"node-notifier/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"nypm/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||
|
||||
"oxc-parser/@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="],
|
||||
|
||||
"package-json/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||
|
||||
"parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||
@@ -2704,6 +2724,8 @@
|
||||
|
||||
"publish-browser-extension/cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
|
||||
"publish-browser-extension/dotenv": ["dotenv@17.2.4", "", {}, "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw=="],
|
||||
|
||||
"rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
||||
@@ -2722,6 +2744,8 @@
|
||||
|
||||
"sass/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
|
||||
|
||||
"solid-refresh/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
||||
@@ -2730,24 +2754,36 @@
|
||||
|
||||
"strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
|
||||
|
||||
"tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
"tsdown/defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"tsdown/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"tsdown/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"typedoc/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"unconfig/defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"unimport/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"unimport/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"unimport/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"unimport/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"unplugin/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"unplugin-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"unplugin-utils/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"update-notifier/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"vite/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"vite/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"vite-node/cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
|
||||
"vite-node/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
@@ -2760,11 +2796,15 @@
|
||||
|
||||
"vitepress/vue": ["vue@3.5.28", "", { "dependencies": { "@vue/compiler-dom": "3.5.28", "@vue/compiler-sfc": "3.5.28", "@vue/runtime-dom": "3.5.28", "@vue/server-renderer": "3.5.28", "@vue/shared": "3.5.28" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg=="],
|
||||
|
||||
"vitepress-plugin-llms/markdown-it": ["markdown-it@14.1.1", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="],
|
||||
|
||||
"vitepress-plugin-llms/mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="],
|
||||
|
||||
"vitest/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="],
|
||||
|
||||
"vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"vitest/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
"vitest/tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||
|
||||
"vue/@vue/shared": ["@vue/shared@3.5.34", "", {}, "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA=="],
|
||||
|
||||
@@ -2774,7 +2814,7 @@
|
||||
|
||||
"wsl-utils/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||
|
||||
"wxt/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
"wxt/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
||||
|
||||
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
@@ -2814,15 +2854,25 @@
|
||||
|
||||
"boxen/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"c12/giget/citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
||||
"boxen/string-width/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"changelogen/c12/defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"changelogen/c12/dotenv": ["dotenv@17.2.4", "", {}, "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw=="],
|
||||
|
||||
"changelogen/c12/giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
|
||||
|
||||
"changelogen/c12/rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
|
||||
|
||||
"changelogen/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||
|
||||
"cli-truncate/string-width/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"configstore/dot-prop/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
"cspell/tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"fx-runner/which/isexe": ["isexe@1.1.2", "", {}, "sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw=="],
|
||||
|
||||
@@ -2838,8 +2888,16 @@
|
||||
|
||||
"sass/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"slice-ansi/is-fullwidth-code-point/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"tsdown/tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"typedoc/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"vitepress-plugin-llms/markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"vitepress-plugin-llms/markdown-it/linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
|
||||
|
||||
"vitepress/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
||||
|
||||
"vitepress/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.28", "", { "dependencies": { "@vue/compiler-core": "3.5.28", "@vue/shared": "3.5.28" } }, "sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA=="],
|
||||
@@ -2852,8 +2910,12 @@
|
||||
|
||||
"widest-line/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"widest-line/string-width/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"wrap-ansi/string-width/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"@vueuse/core/vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.28", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.28", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ=="],
|
||||
@@ -2904,6 +2966,8 @@
|
||||
|
||||
"ast-kit/@babel/parser/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@8.0.0-rc.1", "", {}, "sha512-I4YnARytXC2RzkLNVnf5qFNFMzp679qZpmtw/V3Jt2uGnWiIxyJtaukjG7R8pSx8nG2NamICpGfljQsogj+FbQ=="],
|
||||
|
||||
"changelogen/c12/giget/citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
||||
|
||||
"changelogen/open/wsl-utils/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||
|
||||
"multimatch/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
@@ -32,6 +32,7 @@ words:
|
||||
- consolas
|
||||
- Cpath
|
||||
- crbug
|
||||
- cros
|
||||
- crunchyroll
|
||||
- cssinjs
|
||||
- CSUI
|
||||
@@ -69,6 +70,7 @@ words:
|
||||
- Menlo
|
||||
- menupanel
|
||||
- metas
|
||||
- moderok
|
||||
- mktemp
|
||||
- mycompany
|
||||
- nacl
|
||||
|
||||
@@ -18,10 +18,8 @@ const posts = computed<Post[]>(() =>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div>
|
||||
<div class="vp-doc">
|
||||
<h1>Blog</h1>
|
||||
</div>
|
||||
<div class="inner-container">
|
||||
<h1>Blog</h1>
|
||||
|
||||
<ul>
|
||||
<BlogPostPreview v-for="post of posts" :key="post.url" :post />
|
||||
@@ -35,37 +33,33 @@ const posts = computed<Post[]>(() =>
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.container > div {
|
||||
padding: 32px;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.inner-container {
|
||||
padding: 32px;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
list-style: none;
|
||||
}
|
||||
ul,
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
margin-top: 10px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
padding-top: 16px;
|
||||
margin-top: 16px;
|
||||
border-top: 1px solid var(--vp-c-default);
|
||||
}
|
||||
ul li:last-child {
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--vp-c-default);
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
li {
|
||||
padding-top: 16px;
|
||||
margin-top: 16px;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { Ref } from 'vue';
|
||||
import useBlogDate from '../composables/useBlogDate';
|
||||
import { Content, useData } from 'vitepress';
|
||||
import { PostFrontmatter } from '../utils/types';
|
||||
|
||||
const { frontmatter } = useData<PostFrontmatter>();
|
||||
const { frontmatter } = useData() as unknown as {
|
||||
frontmatter: Ref<PostFrontmatter>;
|
||||
};
|
||||
|
||||
const date = useBlogDate(() => frontmatter.value.date);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-doc">
|
||||
<main class="container-content">
|
||||
<main class="container">
|
||||
<h1 v-html="frontmatter.title" />
|
||||
<p class="meta-row">
|
||||
<a
|
||||
@@ -18,7 +22,10 @@ const date = useBlogDate(() => frontmatter.value.date);
|
||||
:href="`https://github.com/${author.github}`"
|
||||
class="author"
|
||||
>
|
||||
<img :src="`https://github.com/${author.github}.png?size=96`" />
|
||||
<img
|
||||
:src="`https://github.com/${author.github}.png?size=96`"
|
||||
alt="Author's avatar"
|
||||
/>
|
||||
<span>{{ author.name }}</span>
|
||||
</a>
|
||||
<span>•</span>
|
||||
@@ -30,48 +37,43 @@ const date = useBlogDate(() => frontmatter.value.date);
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
vp-doc {
|
||||
.vp-doc {
|
||||
display: flex;
|
||||
}
|
||||
main {
|
||||
max-width: 1080px;
|
||||
padding: 32px;
|
||||
margin: auto;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
main {
|
||||
padding: 64px;
|
||||
|
||||
.container {
|
||||
max-width: 1080px;
|
||||
padding: 32px;
|
||||
margin: auto;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
padding: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.meta-row {
|
||||
display: flex;
|
||||
color: var(--vp-c-text-2);
|
||||
gap: 16px;
|
||||
overflow: hidden;
|
||||
padding-bottom: 32px;
|
||||
|
||||
.author {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
color: var(--vp-c-text-2);
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
|
||||
:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.meta-row {
|
||||
display: flex;
|
||||
color: var(--vp-c-text-2);
|
||||
gap: 16px;
|
||||
overflow: hidden;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
.meta-row > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.author {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
color: var(--vp-c-text-2);
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
.author img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
.author span {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.author:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -31,42 +31,23 @@ const date = useBlogDate(() => props.post.date);
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.vp-doc {
|
||||
* {
|
||||
margin-block: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
.title {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
li > a > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 -16px;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
li > a > div:hover {
|
||||
background: var(--vp-c-default);
|
||||
}
|
||||
li .title {
|
||||
color: var(--vp-c-text);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
li .description {
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
li .meta {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-2);
|
||||
.description {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,36 +5,61 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<table class="no-vertical-dividers">
|
||||
<table class="no-vertical-dividers entrypoint-patterns">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 100%">Filename</th>
|
||||
<th class="col-filename">Filename</th>
|
||||
<th></th>
|
||||
<th>Output Path</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="pattern of patterns">
|
||||
<td style="white-space: nowrap; padding-right: 8px">
|
||||
<tr v-for="pattern of patterns" :key="pattern[0]">
|
||||
<td class="cell-input">
|
||||
<code>entrypoints/{{ pattern[0] }}</code>
|
||||
</td>
|
||||
<td style="padding: 6px; opacity: 50%">
|
||||
<td class="cell-arrow">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M4 11v2h12l-5.5 5.5l1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5L16 11H4Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</td>
|
||||
<td style="white-space: nowrap; padding-left: 8px">
|
||||
<td class="cell-output">
|
||||
<code>/{{ pattern[1] }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.entrypoint-patterns {
|
||||
width: 100%;
|
||||
|
||||
.col-filename {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cell-input {
|
||||
white-space: nowrap;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.cell-arrow {
|
||||
padding: 6px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cell-output {
|
||||
white-space: nowrap;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,7 +8,12 @@ defineProps<{
|
||||
tag?: string;
|
||||
}>();
|
||||
|
||||
const exampleMetadata = ref<ExamplesMetadata>();
|
||||
const exampleMetadata = ref<ExamplesMetadata>({
|
||||
allApis: [],
|
||||
allPackages: [],
|
||||
allPermissions: [],
|
||||
examples: [],
|
||||
});
|
||||
onMounted(async () => {
|
||||
const res = await fetch(
|
||||
'https://raw.githubusercontent.com/wxt-dev/examples/main/metadata.json',
|
||||
@@ -72,19 +77,19 @@ const filteredExamples = computed(() => {
|
||||
|
||||
<div class="filters">
|
||||
<ExampleSearchFilterByItem
|
||||
label="APIs"
|
||||
:items="exampleMetadata?.allApis"
|
||||
v-model="selectedApis"
|
||||
:items="exampleMetadata?.allApis"
|
||||
label="APIs"
|
||||
/>
|
||||
<ExampleSearchFilterByItem
|
||||
label="Permissions"
|
||||
:items="exampleMetadata?.allPermissions"
|
||||
v-model="selectedPermissions"
|
||||
:items="exampleMetadata?.allPermissions"
|
||||
label="Permissions"
|
||||
/>
|
||||
<ExampleSearchFilterByItem
|
||||
label="Packages"
|
||||
:items="exampleMetadata?.allPackages"
|
||||
v-model="selectedPackages"
|
||||
:items="exampleMetadata?.allPackages"
|
||||
label="Packages"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -107,120 +112,57 @@ const filteredExamples = computed(() => {
|
||||
<style scoped>
|
||||
.example-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas:
|
||||
'search'
|
||||
'results';
|
||||
'filters search'
|
||||
'filters results';
|
||||
gap: 16px;
|
||||
}
|
||||
@media only screen and (min-width: 720px) {
|
||||
.example-layout {
|
||||
|
||||
@media only screen and (min-width: 710px) {
|
||||
grid-template-columns: 256px 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas:
|
||||
'filters search'
|
||||
'filters results';
|
||||
}
|
||||
}
|
||||
.search {
|
||||
grid-area: search;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
border-radius: 16px;
|
||||
}
|
||||
.filters {
|
||||
display: none;
|
||||
grid-area: filters;
|
||||
}
|
||||
@media only screen and (min-width: 720px) {
|
||||
|
||||
.search {
|
||||
grid-area: search;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 20px;
|
||||
border-radius: 16px;
|
||||
|
||||
input {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.filters {
|
||||
grid-area: filters;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
align-self: flex-start;
|
||||
gap: 10px;
|
||||
|
||||
@media only screen and (min-width: 710px) {
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.results {
|
||||
grid-area: results;
|
||||
}
|
||||
|
||||
.box {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.results {
|
||||
grid-area: results;
|
||||
|
||||
.search input {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
}
|
||||
.search-results {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
|
||||
.checkbox-col {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
max-height: 200px;
|
||||
font-size: 14px;
|
||||
gap: 4px;
|
||||
}
|
||||
@media only screen and (min-width: 800px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
@media only screen and (min-width: 1024px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.checkbox-col .header {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
.checkbox-col p {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: flex-start;
|
||||
text-wrap: wrap;
|
||||
overflow-wrap: anywhere;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
span {
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.checkbox-col input[type='checkbox'] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.checkbox-col-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.search-results {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
@media only screen and (min-width: 800px) {
|
||||
.search-results {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
a {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 1024px) {
|
||||
.search-results {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -29,12 +29,12 @@ function toggleItem(pkg: string) {
|
||||
<span>Filter by {{ label }}</span> <span v-if="count">({{ count }})</span>
|
||||
</p>
|
||||
<div class="scroll-container">
|
||||
<ul>
|
||||
<ul class="items">
|
||||
<li v-for="item in items">
|
||||
<label :title="item">
|
||||
<label :title="item" class="item-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="selectedItems[item]"
|
||||
type="checkbox"
|
||||
@input="toggleItem(item)"
|
||||
/>
|
||||
<span>{{ item }}</span>
|
||||
@@ -51,50 +51,45 @@ function toggleItem(pkg: string) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
border-radius: 20px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--vp-c-brand-3) transparent;
|
||||
padding-bottom: 10px;
|
||||
|
||||
.scroll-container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.header {
|
||||
padding: 8px 16px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
.scroll-container ul {
|
||||
position: absolute;
|
||||
overflow-y: auto;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
font-size: small;
|
||||
padding: 8px 16px 16px 16px;
|
||||
}
|
||||
.scroll-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.header {
|
||||
padding: 8px 16px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
opacity: 50%;
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: flex-start;
|
||||
text-wrap: wrap;
|
||||
overflow-wrap: anywhere;
|
||||
line-height: 140%;
|
||||
cursor: pointer;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
span {
|
||||
padding-top: 1px;
|
||||
}
|
||||
input[type='checkbox'] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
.items {
|
||||
position: absolute;
|
||||
overflow-y: auto;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
font-size: small;
|
||||
padding: 8px 16px 16px;
|
||||
|
||||
.item-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
input[type='checkbox'] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,56 +17,45 @@ defineProps<{
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
* {
|
||||
min-width: 0;
|
||||
}
|
||||
a {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 2px solid var(--vp-c-bg-soft);
|
||||
border-radius: 16px;
|
||||
color: var(--vp-c-text-1) !important;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
outline: 2px solid var(--vp-c-brand-2);
|
||||
}
|
||||
.name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 0;
|
||||
&:hover {
|
||||
outline: 2px solid var(--vp-c-brand-2);
|
||||
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.description {
|
||||
height: 53px;
|
||||
opacity: 70%;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 120%;
|
||||
min-height: 0;
|
||||
.link {
|
||||
opacity: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.name {
|
||||
font-weight: 500;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.link {
|
||||
opacity: 0;
|
||||
transition: 250ms;
|
||||
color: var(--vp-c-brand-2);
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
a:hover .link {
|
||||
opacity: 100%;
|
||||
.description {
|
||||
opacity: 70%;
|
||||
font-size: 14px;
|
||||
line-height: 130%;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.link {
|
||||
opacity: 0;
|
||||
transition: 250ms;
|
||||
color: var(--vp-c-brand-2);
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,6 +21,7 @@ interface ListedExtension {
|
||||
const extensionEntries = _extensionEntries as Array<{
|
||||
chromeId?: string;
|
||||
firefoxSlug?: string;
|
||||
edgeId?: string;
|
||||
}>;
|
||||
|
||||
const chromeIds = extensionEntries.flatMap((e) =>
|
||||
@@ -31,8 +32,13 @@ const firefoxSlugs = [
|
||||
extensionEntries.flatMap((e) => (e.firefoxSlug ? [e.firefoxSlug] : [])),
|
||||
),
|
||||
];
|
||||
const edgeIds = extensionEntries.flatMap((e) => (e.edgeId ? [e.edgeId] : []));
|
||||
|
||||
const { data, isLoading } = useListExtensionDetails(chromeIds, firefoxSlugs);
|
||||
const { data, isLoading } = useListExtensionDetails(
|
||||
chromeIds,
|
||||
firefoxSlugs,
|
||||
edgeIds,
|
||||
);
|
||||
|
||||
function addUtmSource(storeUrl: string): string {
|
||||
const url = new URL(storeUrl);
|
||||
@@ -49,6 +55,9 @@ const sortedExtensions = computed((): ListedExtension[] => {
|
||||
const firefoxBySlug = new Map(
|
||||
(data.value.firefox ?? []).filter(Boolean).map((e) => [e.id, e]),
|
||||
);
|
||||
const edgeById = new Map(
|
||||
(data.value.edge ?? []).filter(Boolean).map((e) => [e.id, e]),
|
||||
);
|
||||
|
||||
const results: ListedExtension[] = [];
|
||||
|
||||
@@ -57,12 +66,14 @@ const sortedExtensions = computed((): ListedExtension[] => {
|
||||
const firefox = entry.firefoxSlug
|
||||
? firefoxBySlug.get(entry.firefoxSlug)
|
||||
: undefined;
|
||||
const edge = entry.edgeId ? edgeById.get(entry.edgeId) : undefined;
|
||||
|
||||
if (!chrome && !firefox) continue;
|
||||
if (!chrome && !firefox && !edge) continue;
|
||||
|
||||
const primary = chrome ?? firefox!;
|
||||
const users = (chrome?.users ?? 0) + (firefox?.users ?? 0);
|
||||
const rating = chrome?.rating ?? firefox?.rating;
|
||||
const primary = chrome ?? firefox ?? edge!;
|
||||
const users =
|
||||
(chrome?.users ?? 0) + (firefox?.users ?? 0) + (edge?.users ?? 0);
|
||||
const rating = chrome?.rating ?? firefox?.rating ?? edge?.rating;
|
||||
|
||||
const stores: StoreLink[] = [];
|
||||
if (chrome) {
|
||||
@@ -71,9 +82,16 @@ const sortedExtensions = computed((): ListedExtension[] => {
|
||||
if (firefox) {
|
||||
stores.push({ label: 'Firefox', url: addUtmSource(firefox.storeUrl) });
|
||||
}
|
||||
if (edge) {
|
||||
stores.push({ label: 'Edge', url: addUtmSource(edge.storeUrl) });
|
||||
}
|
||||
|
||||
results.push({
|
||||
id: entry.chromeId ?? `firefox:${entry.firefoxSlug}`,
|
||||
id:
|
||||
entry.chromeId ??
|
||||
(entry.firefoxSlug
|
||||
? `firefox:${entry.firefoxSlug}`
|
||||
: `edge:${entry.edgeId}`),
|
||||
name: primary.name,
|
||||
iconUrl: primary.iconUrl,
|
||||
shortDescription: primary.shortDescription,
|
||||
@@ -124,32 +142,31 @@ function formatStars(r: number): string {
|
||||
{{ extension.shortDescription }}
|
||||
</p>
|
||||
<div v-if="extension.stores.length > 0" class="store-stats">
|
||||
<span class="store-stats-info">
|
||||
<p class="store-stats-info">
|
||||
<span>{{ formatUsers(extension.users) }}</span>
|
||||
<template v-if="extension.rating">
|
||||
<span class="store-stats-sep" aria-hidden="true">,</span>
|
||||
<span>{{ formatStars(extension.rating) }}</span>
|
||||
</template>
|
||||
</span>
|
||||
<span class="store-stats-sep" aria-hidden="true">·</span>
|
||||
<span class="store-links">
|
||||
<a
|
||||
v-for="(store, i) of extension.stores"
|
||||
:key="store.label"
|
||||
:href="store.url"
|
||||
target="_blank"
|
||||
class="store-link"
|
||||
:title="store.label"
|
||||
>
|
||||
{{ store.label
|
||||
}}<span
|
||||
</p>
|
||||
<p class="store-stats-sep" aria-hidden="true">·</p>
|
||||
<p class="store-links">
|
||||
<template v-for="(store, i) of extension.stores" :key="store.label">
|
||||
<a
|
||||
:href="store.url"
|
||||
target="_blank"
|
||||
class="store-link"
|
||||
:title="store.label"
|
||||
>{{ store.label }}</a
|
||||
>
|
||||
<span
|
||||
v-if="i < extension.stores.length - 1"
|
||||
class="store-stats-sep"
|
||||
aria-hidden="true"
|
||||
>, </span
|
||||
>
|
||||
</a>
|
||||
</span>
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -13,10 +13,11 @@ export interface Extension {
|
||||
export interface ExtensionResults {
|
||||
chrome: Extension[];
|
||||
firefox: Extension[];
|
||||
edge: Extension[];
|
||||
}
|
||||
|
||||
const operationName = 'WxtDocsUsedBy';
|
||||
const query = `query ${operationName}($chromeIds: [String!]!, $firefoxIds: [String!]!) {
|
||||
const query = `query ${operationName}($chromeIds: [String!]!, $firefoxIds: [String!]!, $edgeIds: [String!]!) {
|
||||
chromeExtensions(ids: $chromeIds) {
|
||||
id
|
||||
...ExtensionData
|
||||
@@ -25,6 +26,10 @@ const query = `query ${operationName}($chromeIds: [String!]!, $firefoxIds: [Stri
|
||||
id: slug
|
||||
...ExtensionData
|
||||
}
|
||||
edgeAddons(ids: $edgeIds) {
|
||||
id
|
||||
...ExtensionData
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExtensionData on Extension {
|
||||
@@ -36,13 +41,21 @@ fragment ExtensionData on Extension {
|
||||
users
|
||||
}`;
|
||||
|
||||
export default function (chromeIds: string[], firefoxSlugs: string[]) {
|
||||
export default function (
|
||||
chromeIds: string[],
|
||||
firefoxSlugs: string[],
|
||||
edgeIds: string[],
|
||||
) {
|
||||
const data = ref<ExtensionResults>();
|
||||
const err = ref<unknown>();
|
||||
const isLoading = ref(true);
|
||||
|
||||
if (chromeIds.length === 0 && firefoxSlugs.length === 0) {
|
||||
data.value = { chrome: [], firefox: [] };
|
||||
if (
|
||||
chromeIds.length === 0 &&
|
||||
firefoxSlugs.length === 0 &&
|
||||
edgeIds.length === 0
|
||||
) {
|
||||
data.value = { chrome: [], firefox: [], edge: [] };
|
||||
isLoading.value = false;
|
||||
return { data, err, isLoading };
|
||||
}
|
||||
@@ -52,7 +65,7 @@ export default function (chromeIds: string[], firefoxSlugs: string[]) {
|
||||
body: JSON.stringify({
|
||||
operationName,
|
||||
query,
|
||||
variables: { chromeIds, firefoxIds: firefoxSlugs },
|
||||
variables: { chromeIds, firefoxIds: firefoxSlugs, edgeIds },
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -64,6 +77,7 @@ export default function (chromeIds: string[], firefoxSlugs: string[]) {
|
||||
data.value = {
|
||||
chrome: responseData.chromeExtensions ?? [],
|
||||
firefox: responseData.firefoxAddons ?? [],
|
||||
edge: responseData.edgeAddons ?? [],
|
||||
};
|
||||
err.value = undefined;
|
||||
})
|
||||
|
||||
@@ -163,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:
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
/* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */
|
||||
/* Colors */
|
||||
:root {
|
||||
--wxt-c-green-1: #0b8a00;
|
||||
--wxt-c-green-2: #096600;
|
||||
--wxt-c-green-3: #096600;
|
||||
--wxt-c-green-soft: rgba(11, 138, 0, 0.14);
|
||||
|
||||
--vp-c-brand-1: var(--wxt-c-green-1);
|
||||
--vp-c-brand-2: var(--wxt-c-green-2);
|
||||
--vp-c-brand-3: var(--wxt-c-green-3);
|
||||
--vp-c-brand-soft: var(--wxt-c-green-soft);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -13,72 +19,56 @@
|
||||
--wxt-c-green-soft: rgba(103, 212, 94, 0.14);
|
||||
}
|
||||
|
||||
/* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */
|
||||
|
||||
:root {
|
||||
--vp-c-brand-1: var(--wxt-c-green-1);
|
||||
--vp-c-brand-2: var(--wxt-c-green-2);
|
||||
--vp-c-brand-3: var(--wxt-c-green-3);
|
||||
--vp-c-brand-soft: var(--wxt-c-green-soft);
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Customize Individual Components */
|
||||
.vp-doc .no-vertical-dividers {
|
||||
th,
|
||||
td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.vp-doc .no-vertical-dividers th,
|
||||
.vp-doc .no-vertical-dividers td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.vp-doc .no-vertical-dividers tr {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
tr {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.VPSidebar {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.VPSidebarItem .badge {
|
||||
display: inline-block;
|
||||
min-width: 1.6em;
|
||||
padding: 0.3em 0.4em 0.2em;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.75em;
|
||||
line-height: 1;
|
||||
margin-left: 0.5rem;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
background-color: var(--vp-c-default-2);
|
||||
}
|
||||
|
||||
.light-netlify {
|
||||
display: inline;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.dark .light-netlify {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark-netlify {
|
||||
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. */
|
||||
.dark .VPHome::before {
|
||||
content: '';
|
||||
background-image: url('/assets/art002e009298.jpg');
|
||||
opacity: 50%;
|
||||
position: absolute;
|
||||
background-image: url('../../assets/art002e009298.jpg');
|
||||
opacity: 50%;
|
||||
width: 100%;
|
||||
height: 110vh;
|
||||
background-size: cover;
|
||||
background-position: 90%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dark .VPHome::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -86,9 +76,8 @@ body {
|
||||
height: 10vh;
|
||||
top: 100vh;
|
||||
background: linear-gradient(to bottom, transparent, var(--vp-c-bg));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.item {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import BlogLayout from '../components/BlogLayout.vue';
|
||||
import './custom.css';
|
||||
import 'virtual:group-icons.css';
|
||||
import type { EnhanceAppContext } from 'vitepress/client';
|
||||
import { setupCodeGroupSync } from '../utils/code-groups-sync';
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
@@ -17,5 +18,7 @@ export default {
|
||||
.component('UsingWxtSection', UsingWxtSection)
|
||||
.component('ExampleSearch', ExampleSearch)
|
||||
.component('blog', BlogLayout);
|
||||
|
||||
setupCodeGroupSync();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
const CODE_GROUP_SELECTORS = {
|
||||
root: '.vp-code-group',
|
||||
input: '.vp-code-group .tabs input',
|
||||
label: '.tabs label',
|
||||
} as const;
|
||||
|
||||
function getTabTitle(label: HTMLLabelElement) {
|
||||
return label.dataset.title?.trim() || label.textContent?.trim();
|
||||
}
|
||||
|
||||
function getCodeGroupTitle(input: HTMLInputElement) {
|
||||
const label = input.parentElement?.querySelector<HTMLLabelElement>(
|
||||
`label[for="${input.id}"]`,
|
||||
);
|
||||
|
||||
return label ? getTabTitle(label) : undefined;
|
||||
}
|
||||
|
||||
function findCodeGroupTabIndex(group: HTMLElement, title: string) {
|
||||
return Array.from(
|
||||
group.querySelectorAll<HTMLLabelElement>(CODE_GROUP_SELECTORS.label),
|
||||
).findIndex((label) => getTabTitle(label) === title);
|
||||
}
|
||||
|
||||
function syncCodeGroupSelection(group: HTMLElement, title: string) {
|
||||
const targetIndex = findCodeGroupTabIndex(group, title);
|
||||
|
||||
if (targetIndex < 0) return;
|
||||
|
||||
const blocks = group.querySelector('.blocks');
|
||||
const targetBlock = blocks?.children[targetIndex];
|
||||
if (!blocks || !targetBlock || targetBlock.classList.contains('active'))
|
||||
return;
|
||||
|
||||
blocks.querySelector('.active')?.classList.remove('active');
|
||||
targetBlock.classList.add('active');
|
||||
|
||||
const input = group.querySelectorAll<HTMLInputElement>(
|
||||
CODE_GROUP_SELECTORS.input,
|
||||
)[targetIndex];
|
||||
if (input) input.checked = true;
|
||||
}
|
||||
|
||||
function syncCodeGroupTabs(event: Event) {
|
||||
const target = event.target;
|
||||
if (
|
||||
!(target instanceof HTMLInputElement) ||
|
||||
!target.matches(CODE_GROUP_SELECTORS.input)
|
||||
)
|
||||
return;
|
||||
|
||||
const activeGroup = target.closest(CODE_GROUP_SELECTORS.root);
|
||||
const activeTitle = getCodeGroupTitle(target);
|
||||
if (!activeTitle) return;
|
||||
|
||||
document
|
||||
.querySelectorAll<HTMLElement>(CODE_GROUP_SELECTORS.root)
|
||||
.forEach((group) => {
|
||||
if (group === activeGroup) return;
|
||||
|
||||
syncCodeGroupSelection(group, activeTitle);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps every code group on the page in sync: selecting a package manager (or
|
||||
* any tab) in one group switches the matching tab in all the other groups.
|
||||
*/
|
||||
export function setupCodeGroupSync() {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
window.addEventListener('change', syncCodeGroupTabs, true);
|
||||
}
|
||||
@@ -7,10 +7,15 @@
|
||||
# Use:
|
||||
#
|
||||
# - `chromeId` for the Chrome Web Store listing,
|
||||
# - `firefoxSlug` for Firefox Add-ons (slug from https://addons.mozilla.org/firefox/addon/{slug}/)
|
||||
# - `firefoxSlug` for Firefox Add-ons (slug from https://addons.mozilla.org/firefox/addon/{slug}/),
|
||||
# - `edgeId` for the Edge Add-ons listing (ID from https://microsoftedge.microsoft.com/addons/detail/{slug}/{id})
|
||||
#
|
||||
# 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
|
||||
@@ -26,6 +31,7 @@
|
||||
- # StayFree - Website Blocker & Web Analytics
|
||||
chromeId: elfaihghhjjoknimpccccmkioofjjfkf
|
||||
firefoxSlug: stayfree
|
||||
edgeId: aghmglalnfmjbipodadgbgpakneabgnp
|
||||
|
||||
- # Doozy: Ai Made Easy
|
||||
chromeId: okifoaikfmpfcamplcfjkpdnhfodpkil
|
||||
@@ -105,6 +111,8 @@
|
||||
|
||||
- # NiceTab - https://github.com/web-dahuyou/NiceTab
|
||||
chromeId: fonflmjnjbkigocpoommgmhljdpljain
|
||||
firefoxSlug: nice-tab-manager
|
||||
edgeId: ompjiaelpibiggcnanhbdblkhfdmkgnl
|
||||
|
||||
- # Draftly for LinkedIn
|
||||
chromeId: fcffekbnfcfdemeekijbbmgmkognnmkd
|
||||
@@ -138,6 +146,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 +241,7 @@
|
||||
|
||||
- # Monochromate
|
||||
chromeId: hafcajcllbjnoolpfngclfmmgpikdhlm
|
||||
firefoxSlug: monochromate
|
||||
|
||||
- # AliasVault - Open-Source Password & (Email) Alias Manager
|
||||
chromeId: bmoggiinmnodjphdjnmpcnlleamkfedj
|
||||
@@ -458,3 +483,38 @@
|
||||
- # 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
|
||||
|
||||
- # CopyApes Assistant
|
||||
chromeId: affmjifigldmicnbgpghddaneomejmfo
|
||||
|
||||
- # Leetcode Enhancer
|
||||
chromeId: cpoclfijojgjiafnlgnhjalkaiabcjch
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 132 KiB |
@@ -17,28 +17,14 @@ Or if you're in a monorepo, you may not want to extend the config. If you don't
|
||||
/// <reference path="./.wxt/wxt.d.ts" />
|
||||
```
|
||||
|
||||
## Compiler Options
|
||||
|
||||
To specify custom compiler options, add them in `<rootDir>/tsconfig.json`:
|
||||
|
||||
```jsonc
|
||||
// <rootDir>/tsconfig.json
|
||||
{
|
||||
"extends": ".wxt/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## TSConfig Paths
|
||||
|
||||
WXT provides a default set of path aliases.
|
||||
|
||||
| Alias | To | Example |
|
||||
| ----- | ------------- | ----------------------------------------------- |
|
||||
| `~~` | `<rootDir>/*` | `import "~~/scripts"` |
|
||||
| `@@` | `<rootDir>/*` | `import "@@/scripts"` |
|
||||
| `~~` | `<rootDir>/*` | `import "~~/package.json"` |
|
||||
| `@@` | `<rootDir>/*` | `import "@@/package.json"` |
|
||||
| `~` | `<srcDir>/*` | `import { toLowerCase } from "~/utils/strings"` |
|
||||
| `@` | `<srcDir>/*` | `import { toLowerCase } from "@/utils/strings"` |
|
||||
|
||||
@@ -63,3 +49,34 @@ export default defineConfig({
|
||||
import { fakeTab } from 'testing/fake-objects';
|
||||
import { toLowerCase } from 'strings';
|
||||
```
|
||||
|
||||
## Custom Options
|
||||
|
||||
To specify custom compiler or top-level options, you have two options:
|
||||
|
||||
1. Override WXT's value completely by setting it in your root `tsconfig.json`:
|
||||
|
||||
```jsonc
|
||||
// <rootDir>/tsconfig.json
|
||||
{
|
||||
"extends": ".wxt/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
2. Merge custom values into values generated by WXT via the `prepare:tsconfig` hook:
|
||||
|
||||
```ts
|
||||
// wxt.config.ts
|
||||
export default defineConfig({
|
||||
hooks: {
|
||||
'prepare:tsconfig': (wxt, { tsconfig }) => {
|
||||
tsconfig.compilerOptions.lib.push('WebWorker');
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Overriding is great for simple boolean values, but not for complex options like `paths` or `libs`, where you can't just "add" a value to the object from your `tsconfig.json` file. So WXT provides the hook that let's you add or merge or delete or do whatever you want to the config file before it's written to the `.wxt/` directory.
|
||||
|
||||
@@ -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_**.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -78,3 +78,28 @@ Here are some examples:
|
||||
```
|
||||
|
||||
Alternatively, you can use the [`filterEntrypoints` config](/api/reference/wxt/interfaces/InlineConfig#filterentrypoints) to list all the entrypoints you want to build.
|
||||
|
||||
## Per-Browser Options
|
||||
|
||||
Some entrypoint options can be customized per build target by passing an object keyed by the [target browser](/guide/essentials/target-different-browsers#target-a-browser) instead of a single value. This is useful when different browsers need different match patterns, run timings, or other entrypoint behavior:
|
||||
|
||||
```ts
|
||||
export default defineContentScript({
|
||||
matches: {
|
||||
chrome: ['*://chrome.example.com/*'],
|
||||
firefox: ['*://firefox.example.com/*'],
|
||||
},
|
||||
runAt: {
|
||||
chrome: 'document_start',
|
||||
firefox: 'document_end',
|
||||
},
|
||||
|
||||
world: {
|
||||
firefox: 'MAIN',
|
||||
},
|
||||
|
||||
main(ctx) {
|
||||
// ...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
+17
-17
@@ -10,14 +10,14 @@ Run the [`init` command](/api/cli/wxt-init), and follow the instructions.
|
||||
|
||||
:::code-group
|
||||
|
||||
```sh [PNPM]
|
||||
pnpm dlx wxt@latest init
|
||||
```
|
||||
|
||||
```sh [Bun]
|
||||
bunx wxt@latest init
|
||||
```
|
||||
|
||||
```sh [PNPM]
|
||||
pnpm dlx wxt@latest init
|
||||
```
|
||||
|
||||
```sh [NPM]
|
||||
npx wxt@latest init
|
||||
```
|
||||
@@ -46,16 +46,16 @@ Once you've run the `dev` command, continue to [Next Steps](#next-steps)!
|
||||
1. Create a new project
|
||||
:::code-group
|
||||
|
||||
```sh [PNPM]
|
||||
cd my-project
|
||||
pnpm init
|
||||
```
|
||||
|
||||
```sh [Bun]
|
||||
cd my-project
|
||||
bun init
|
||||
```
|
||||
|
||||
```sh [PNPM]
|
||||
cd my-project
|
||||
pnpm init
|
||||
```
|
||||
|
||||
```sh [NPM]
|
||||
cd my-project
|
||||
npm init
|
||||
@@ -71,14 +71,14 @@ Once you've run the `dev` command, continue to [Next Steps](#next-steps)!
|
||||
2. Install WXT:
|
||||
:::code-group
|
||||
|
||||
```sh [PNPM]
|
||||
pnpm i -D wxt
|
||||
```
|
||||
|
||||
```sh [Bun]
|
||||
bun add -D wxt
|
||||
```
|
||||
|
||||
```sh [PNPM]
|
||||
pnpm i -D wxt
|
||||
```
|
||||
|
||||
```sh [NPM]
|
||||
npm i -D wxt
|
||||
```
|
||||
@@ -119,14 +119,14 @@ Once you've run the `dev` command, continue to [Next Steps](#next-steps)!
|
||||
5. Run your extension in dev mode
|
||||
:::code-group
|
||||
|
||||
```sh [PNPM]
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
```sh [Bun]
|
||||
bun run dev
|
||||
```
|
||||
|
||||
```sh [PNPM]
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
```sh [NPM]
|
||||
npm run dev
|
||||
```
|
||||
|
||||
@@ -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/).
|
||||
@@ -191,6 +221,18 @@ To run the WXT dev server in a devcontainer, but load the dev build of your exte
|
||||
3. **Tell WXT to listen on all network interfaces**
|
||||
To enable hot-reloading, your extension has to connect to the WXT dev server running inside your container. WXT will only listen on `localhost` by default, which prevents connections from outside the devcontainer. To fix this you can instruct WXT to listen on all interfaces with `wxt --host 0.0.0.0`.
|
||||
|
||||
4. **Enable polling if file changes are not detected**
|
||||
Some container, WSL, and network file system setups do not emit native file events reliably. Configure Chokidar polling in `wxt.config.ts`:
|
||||
|
||||
```ts
|
||||
export default defineConfig({
|
||||
watchOptions: {
|
||||
usePolling: true,
|
||||
interval: 1000,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## How do I use the new Prompt API in Chrome?
|
||||
|
||||
The service responsible for the [Prompt API](https://developer.chrome.com/docs/ai/prompt-api) is not enabled by default if you let WXT open the browser during dev mode. When checking `LanguageModel.availability`, you will always receive "unavailable".
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -7,7 +7,7 @@
|
||||
Output docs/assets/init-demo.gif
|
||||
|
||||
# The tools we will use
|
||||
Require pnpm
|
||||
Require bun
|
||||
|
||||
|
||||
# === Scene ====
|
||||
@@ -35,19 +35,19 @@ Env PS1 "\e[0;32m●\e[0m "
|
||||
|
||||
Hide
|
||||
# Create a temporary folder for demo
|
||||
Type 'vhs_sandbox="$(mktemp -d)"' Enter
|
||||
Type 'cd "$vhs_sandbox"' Enter
|
||||
Type 'mkdir packages/wxt/init-demo' Enter
|
||||
Type 'cd packages/wxt/init-demo' Enter
|
||||
Type 'clear' Enter
|
||||
Show
|
||||
|
||||
|
||||
# ===== Actions =====
|
||||
|
||||
Type 'pnpm dlx wxt@latest init .' Sleep 1s Enter
|
||||
Type 'bunx wxt init .' Sleep 1s Enter
|
||||
Sleep 3.25s
|
||||
Down@750ms Enter@750ms # Select `vue` template
|
||||
Sleep 1.25s
|
||||
Down@750ms Enter@750ms # Select `pnpm` as a package manager
|
||||
Down@500ms Down@500ms Enter@750ms # Select `pnpm` as a package manager
|
||||
Sleep 5s
|
||||
|
||||
|
||||
@@ -55,5 +55,6 @@ Sleep 5s
|
||||
|
||||
Hide
|
||||
# Delete the temporary folder
|
||||
Type 'rm -rf "$vhs_sandbox"' Enter
|
||||
Type 'cd .."' Enter
|
||||
Type 'rm -rf init-demo' Enter
|
||||
Show
|
||||
|
||||
+7
-7
@@ -37,25 +37,25 @@
|
||||
"devDependencies": {
|
||||
"@aklinker1/buildc": "catalog:",
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@commitlint/config-conventional": "^20.4.3",
|
||||
"@commitlint/types": "^20.4.3",
|
||||
"@commitlint/config-conventional": "^21.2.0",
|
||||
"@commitlint/types": "^21.2.0",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@vitest/coverage-v8": "^4.1.5",
|
||||
"@vitest/coverage-v8": "^4.1.6",
|
||||
"changelogen": "^0.6.2",
|
||||
"consola": "^3.4.2",
|
||||
"cspell": "^9.7.0",
|
||||
"feed": "^5.2.0",
|
||||
"feed": "^5.2.1",
|
||||
"markdown-it-footnote": "^4.0.0",
|
||||
"markdownlint-cli": "^0.48.0",
|
||||
"markdownlint-cli": "^0.49.0",
|
||||
"nano-spawn": "catalog:",
|
||||
"nano-staged": "^1.0.2",
|
||||
"p-map": "^7.0.4",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-jsdoc": "^1.8.0",
|
||||
"semver": "^7.7.4",
|
||||
"semver": "^7.8.0",
|
||||
"simple-git-hooks": "^2.13.1",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyglobby": "^0.2.16",
|
||||
"tsdown": "catalog:",
|
||||
"tsx": "4.21.0",
|
||||
"typedoc": "^0.25.4",
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## v0.5.5
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/analytics-v0.5.4...analytics-v0.5.5)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- **analytics:** Add Moderok analytics provider ([#2393](https://github.com/wxt-dev/wxt/pull/2393))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- **analytics:** Change method return types from void to Promise<void> ([#2409](https://github.com/wxt-dev/wxt/pull/2409))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Add `prettier-plugin-jsdoc` to project ([#2171](https://github.com/wxt-dev/wxt/pull/2171))
|
||||
- **deps:** Upgrade deps ([#2175](https://github.com/wxt-dev/wxt/pull/2175))
|
||||
- Add prepack script to all packages ([032f7931](https://github.com/wxt-dev/wxt/commit/032f7931))
|
||||
- Use `deps.neverBundle` instead of `external` ([8f71e174](https://github.com/wxt-dev/wxt/commit/8f71e174))
|
||||
- Migrate monorepo to use Bun instead of PNPM ([#2009](https://github.com/wxt-dev/wxt/pull/2009))
|
||||
- **deps-dev:** Bump typescript from 5.9.3 to 6.0.3 ([#2325](https://github.com/wxt-dev/wxt/pull/2325))
|
||||
- Use `catalog:` for dev dependencies ([#2357](https://github.com/wxt-dev/wxt/pull/2357))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Muhammed Mustafa AKŞAM ([@muhammedaksam](https://github.com/muhammedaksam))
|
||||
- Vaughn Bosu ([@VaughnBosu](https://github.com/VaughnBosu))
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
|
||||
## v0.5.4
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/analytics-v0.5.3...analytics-v0.5.4)
|
||||
|
||||
@@ -5,6 +5,7 @@ Report analytics events from your web extension extension.
|
||||
## Supported Analytics Providers
|
||||
|
||||
- [Google Analytics 4 (Measurement Protocol)](#google-analytics-4-measurement-protocol)
|
||||
- [Moderok](#moderok)
|
||||
- [Umami](#umami)
|
||||
|
||||
## Install With WXT
|
||||
@@ -120,6 +121,41 @@ export default defineAppConfig({
|
||||
});
|
||||
```
|
||||
|
||||
### Moderok
|
||||
|
||||
[Moderok](https://moderok.dev) is an analytics platform built specifically for browser extensions. It requires no `host_permissions`, works in Manifest V3 service workers, and collects only anonymous usage data.
|
||||
|
||||
Sign up at [moderok.dev](https://moderok.dev) to get your app key, then save it to your `.env` file:
|
||||
|
||||
```dotenv
|
||||
WXT_MODEROK_APP_KEY='mk_...'
|
||||
```
|
||||
|
||||
Then add the `moderok` provider to your `<srcDir>/app.config.ts` file:
|
||||
|
||||
```ts
|
||||
import { moderok } from '@wxt-dev/analytics/providers/moderok';
|
||||
|
||||
export default defineAppConfig({
|
||||
analytics: {
|
||||
providers: [
|
||||
moderok({
|
||||
appKey: import.meta.env.WXT_MODEROK_APP_KEY,
|
||||
// Automatically track first open, install, update, and daily ping events (default: true)
|
||||
trackLifecycle: true,
|
||||
// Track when users uninstall the extension (default: false)
|
||||
trackUninstalls: false,
|
||||
// Optional: when trackUninstalls is on, redirect users to this page
|
||||
// after they uninstall (e.g. a feedback survey)
|
||||
uninstallUrl: 'https://example.com/uninstall',
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For a full walkthrough — module setup, sending events, and all provider options — see the [Moderok WXT guide](https://docs.moderok.dev/guide/wxt).
|
||||
|
||||
### Umami
|
||||
|
||||
[Umami](https://umami.is/) is a privacy-first, open source analytics platform.
|
||||
|
||||
@@ -25,7 +25,7 @@ type AnalyticsMethod =
|
||||
|
||||
type MethodForwarder = <K extends keyof Analytics>(
|
||||
fn: K,
|
||||
) => (...args: Parameters<Analytics[K]>) => void;
|
||||
) => (...args: Parameters<Analytics[K]>) => Promise<void>;
|
||||
|
||||
const ANALYTICS_PORT = '@wxt-dev/analytics';
|
||||
|
||||
@@ -84,9 +84,13 @@ function createBackgroundAnalytics(
|
||||
// Cached values
|
||||
const platformInfo = browser.runtime.getPlatformInfo();
|
||||
const userAgent = UAParser();
|
||||
let userId = Promise.resolve(userIdStorage.getValue()).then(
|
||||
(id) => id ?? globalThis.crypto.randomUUID(),
|
||||
);
|
||||
let userId = Promise.resolve(userIdStorage.getValue()).then(async (id) => {
|
||||
if (id != null) return id;
|
||||
// Persist the generated ID so it's stable across service worker restarts.
|
||||
const generatedId = globalThis.crypto.randomUUID();
|
||||
await userIdStorage.setValue?.(generatedId);
|
||||
return generatedId;
|
||||
});
|
||||
let userProperties = userPropertiesStorage.getValue();
|
||||
const manifest = browser.runtime.getManifest();
|
||||
|
||||
@@ -238,6 +242,7 @@ function createFrontendAnalytics(): Analytics {
|
||||
(fn) =>
|
||||
(...args) => {
|
||||
port.postMessage({ fn, args: [...args, getFrontendMetadata()] });
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const analytics: Analytics = {
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
import { defineAnalyticsProvider } from '../client';
|
||||
import { browser } from '@wxt-dev/browser';
|
||||
import type { BaseAnalyticsEvent } from '../types';
|
||||
|
||||
const SDK_VERSION = 'wxt/0.1.0';
|
||||
const DEFAULT_ENDPOINT = 'https://api.moderok.dev/v1/events';
|
||||
const PING_STORAGE_KEY = 'moderok:last-ping-date';
|
||||
const FIRST_OPEN_STORAGE_KEY = 'moderok:first-open';
|
||||
|
||||
export interface ModerokProviderOptions {
|
||||
appKey: string;
|
||||
endpoint?: string;
|
||||
trackLifecycle?: boolean;
|
||||
trackUninstalls?: boolean;
|
||||
uninstallUrl?: string;
|
||||
}
|
||||
|
||||
const OS_MAP: Record<string, string> = {
|
||||
mac: 'MacOS',
|
||||
win: 'Windows',
|
||||
linux: 'Linux',
|
||||
cros: 'ChromeOS',
|
||||
android: 'Android',
|
||||
};
|
||||
|
||||
const BROWSER_MAP: Record<string, string> = {
|
||||
chrome: 'chrome',
|
||||
edge: 'edge',
|
||||
firefox: 'firefox',
|
||||
chromium: 'other_chromium',
|
||||
};
|
||||
|
||||
function mapOs(wxtOs: string | undefined): string {
|
||||
if (!wxtOs) return 'unknown';
|
||||
return OS_MAP[wxtOs.toLowerCase()] ?? 'unknown';
|
||||
}
|
||||
|
||||
function mapBrowser(wxtBrowser: string | undefined): string {
|
||||
if (!wxtBrowser) return 'unknown';
|
||||
return BROWSER_MAP[wxtBrowser.toLowerCase()] ?? 'unknown';
|
||||
}
|
||||
|
||||
function detectSource(meta: BaseAnalyticsEvent['meta']): string {
|
||||
if (meta.sessionId == null) return 'background';
|
||||
|
||||
const url = meta.url;
|
||||
if (!url) return 'unknown';
|
||||
const isExtensionUrl =
|
||||
url.startsWith('chrome-extension://') || url.startsWith('moz-extension://');
|
||||
if (!isExtensionUrl) return 'content_script';
|
||||
|
||||
const path = url.toLowerCase();
|
||||
if (path.includes('popup')) return 'popup';
|
||||
if (path.includes('option')) return 'options';
|
||||
if (
|
||||
path.includes('sidepanel') ||
|
||||
path.includes('side-panel') ||
|
||||
path.includes('side_panel')
|
||||
)
|
||||
return 'side_panel';
|
||||
|
||||
return 'extension_page';
|
||||
}
|
||||
|
||||
function buildContext(event: BaseAnalyticsEvent, extensionId: string) {
|
||||
return {
|
||||
sdkVersion: SDK_VERSION,
|
||||
extensionId: extensionId || 'unknown',
|
||||
extensionVersion: event.user.properties.version || 'unknown',
|
||||
browser: mapBrowser(event.user.properties.browser),
|
||||
browserVersion: event.user.properties.browserVersion || 'unknown',
|
||||
os: mapOs(event.user.properties.os),
|
||||
locale: event.meta.language || 'unknown',
|
||||
source: detectSource(event.meta),
|
||||
};
|
||||
}
|
||||
|
||||
function sendEvent(
|
||||
endpoint: string,
|
||||
appKey: string,
|
||||
event: {
|
||||
name: string;
|
||||
userId: string;
|
||||
timestamp: number;
|
||||
context: ReturnType<typeof buildContext>;
|
||||
properties?: Record<string, string | number | boolean>;
|
||||
},
|
||||
debug: boolean,
|
||||
) {
|
||||
const payload = {
|
||||
appKey,
|
||||
sentAt: Date.now(),
|
||||
events: [
|
||||
{
|
||||
id: globalThis.crypto.randomUUID(),
|
||||
name: event.name,
|
||||
timestamp: event.timestamp,
|
||||
userId: event.userId,
|
||||
context: event.context,
|
||||
...(event.properties && Object.keys(event.properties).length > 0
|
||||
? { properties: event.properties }
|
||||
: {}),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (debug) {
|
||||
console.debug('[@wxt-dev/analytics][moderok] Sending:', payload);
|
||||
}
|
||||
|
||||
return fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
function cleanProperties(
|
||||
raw: Record<string, string | undefined> | undefined,
|
||||
): Record<string, string> | undefined {
|
||||
if (!raw) return undefined;
|
||||
const clean: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(raw)) {
|
||||
if (value != null) clean[key] = value;
|
||||
}
|
||||
return Object.keys(clean).length > 0 ? clean : undefined;
|
||||
}
|
||||
|
||||
function utcDateStamp(): string {
|
||||
return new Date().toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function buildUninstallUrl(
|
||||
endpoint: string,
|
||||
appKey: string,
|
||||
userId: string,
|
||||
redirectUrl?: string,
|
||||
): string {
|
||||
const url = new URL(endpoint);
|
||||
url.pathname = url.pathname.replace(/\/[^/]*$/, '/uninstall');
|
||||
url.searchParams.set('app', appKey);
|
||||
url.searchParams.set('uid', userId);
|
||||
if (redirectUrl) url.searchParams.set('redirect', redirectUrl);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export const moderok = defineAnalyticsProvider<ModerokProviderOptions>(
|
||||
(analytics, config, options) => {
|
||||
const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
|
||||
const debug = config.debug ?? false;
|
||||
const trackLifecycle = options.trackLifecycle ?? true;
|
||||
const extensionId = browser.runtime.id ?? '';
|
||||
let firstOpenPromise: Promise<void> | undefined;
|
||||
|
||||
const maybeTrackFirstOpen = async (event: BaseAnalyticsEvent) => {
|
||||
if (!trackLifecycle) return;
|
||||
if (firstOpenPromise) return firstOpenPromise;
|
||||
|
||||
firstOpenPromise = (async () => {
|
||||
const stored = await browser.storage.local.get(FIRST_OPEN_STORAGE_KEY);
|
||||
if (stored[FIRST_OPEN_STORAGE_KEY]) return;
|
||||
|
||||
const response = await sendEvent(
|
||||
endpoint,
|
||||
options.appKey,
|
||||
{
|
||||
name: '__first_open',
|
||||
userId: event.user.id,
|
||||
timestamp: event.meta.timestamp,
|
||||
context: buildContext(event, extensionId),
|
||||
},
|
||||
debug,
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
await browser.storage.local.set({ [FIRST_OPEN_STORAGE_KEY]: true });
|
||||
} else {
|
||||
firstOpenPromise = undefined;
|
||||
}
|
||||
})().catch((error) => {
|
||||
firstOpenPromise = undefined;
|
||||
throw error;
|
||||
});
|
||||
|
||||
return firstOpenPromise;
|
||||
};
|
||||
|
||||
if (trackLifecycle) {
|
||||
browser.runtime.onInstalled.addListener((details) => {
|
||||
if (details.reason === 'install') {
|
||||
analytics.track('__install');
|
||||
} else if (details.reason === 'update') {
|
||||
analytics.track('__update', {
|
||||
previousVersion: (details as { previousVersion?: string })
|
||||
.previousVersion,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
void (async () => {
|
||||
const today = utcDateStamp();
|
||||
const stored = await browser.storage.local.get(PING_STORAGE_KEY);
|
||||
if (stored[PING_STORAGE_KEY] === today) return;
|
||||
await browser.storage.local.set({ [PING_STORAGE_KEY]: today });
|
||||
analytics.track('__daily_ping');
|
||||
})();
|
||||
}
|
||||
|
||||
let uninstallUrlSet = false;
|
||||
function maybeSetUninstallUrl(userId: string) {
|
||||
if (!options.trackUninstalls || uninstallUrlSet) return;
|
||||
uninstallUrlSet = true;
|
||||
|
||||
const url = buildUninstallUrl(
|
||||
endpoint,
|
||||
options.appKey,
|
||||
userId,
|
||||
options.uninstallUrl,
|
||||
);
|
||||
if (url.length <= 1023) {
|
||||
browser.runtime.setUninstallURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
identify: () => Promise.resolve(),
|
||||
|
||||
page: async (event) => {
|
||||
maybeSetUninstallUrl(event.user.id);
|
||||
await maybeTrackFirstOpen(event);
|
||||
await sendEvent(
|
||||
endpoint,
|
||||
options.appKey,
|
||||
{
|
||||
name: '__page_view',
|
||||
userId: event.user.id,
|
||||
timestamp: event.meta.timestamp,
|
||||
context: buildContext(event, extensionId),
|
||||
properties: {
|
||||
url: event.page.url,
|
||||
...(event.page.title ? { title: event.page.title } : {}),
|
||||
...(event.page.location ? { location: event.page.location } : {}),
|
||||
},
|
||||
},
|
||||
debug,
|
||||
);
|
||||
},
|
||||
|
||||
track: async (event) => {
|
||||
maybeSetUninstallUrl(event.user.id);
|
||||
await maybeTrackFirstOpen(event);
|
||||
await sendEvent(
|
||||
endpoint,
|
||||
options.appKey,
|
||||
{
|
||||
name: event.event.name.trim(),
|
||||
userId: event.user.id,
|
||||
timestamp: event.meta.timestamp,
|
||||
context: buildContext(event, extensionId),
|
||||
properties: cleanProperties(event.event.properties),
|
||||
},
|
||||
debug,
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -1,20 +1,23 @@
|
||||
export interface Analytics {
|
||||
/** Report a page change. */
|
||||
page: (url: string) => void;
|
||||
page: (url: string) => Promise<void>;
|
||||
/** Report a custom event. */
|
||||
track: (
|
||||
eventName: string,
|
||||
eventProperties?: Record<string, string | undefined>,
|
||||
) => void;
|
||||
) => Promise<void>;
|
||||
/** Save information about the user. */
|
||||
identify: (userId: string, userProperties?: Record<string, string>) => void;
|
||||
identify: (
|
||||
userId: string,
|
||||
userProperties?: Record<string, string>,
|
||||
) => Promise<void>;
|
||||
/**
|
||||
* Automatically setup and track user interactions, returning a function to
|
||||
* remove any listeners that were setup.
|
||||
*/
|
||||
autoTrack: (root: Document | ShadowRoot | Element) => () => void;
|
||||
/** Calls `config.enabled.setValue`. */
|
||||
setEnabled: (enabled: boolean) => void;
|
||||
setEnabled: (enabled: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface AnalyticsConfig {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wxt-dev/analytics",
|
||||
"version": "0.5.4",
|
||||
"version": "0.5.5",
|
||||
"description": "Add analytics to your web extension",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -55,6 +55,10 @@
|
||||
"types": "./dist/providers/google-analytics-4.d.mts",
|
||||
"default": "./dist/providers/google-analytics-4.mjs"
|
||||
},
|
||||
"./providers/moderok": {
|
||||
"types": "./dist/providers/moderok.d.mts",
|
||||
"default": "./dist/providers/moderok.mjs"
|
||||
},
|
||||
"./providers/umami": {
|
||||
"types": "./dist/providers/umami.d.mts",
|
||||
"default": "./dist/providers/umami.mjs"
|
||||
|
||||
@@ -9,6 +9,7 @@ export default defineConfig({
|
||||
'providers/google-analytics-4':
|
||||
'./modules/analytics/providers/google-analytics-4.ts',
|
||||
'providers/umami': './modules/analytics/providers/umami.ts',
|
||||
'providers/moderok': './modules/analytics/providers/moderok.ts',
|
||||
},
|
||||
deps: {
|
||||
neverBundle: ['#analytics'],
|
||||
|
||||
@@ -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.42",
|
||||
"version": "0.2.0",
|
||||
"type": "module",
|
||||
"main": "src/index.mjs",
|
||||
"types": "src/index.d.ts",
|
||||
@@ -25,7 +25,7 @@
|
||||
"src"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/chrome": "0.1.42",
|
||||
"@types/chrome": "0.2.0",
|
||||
"@types/node": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
|
||||
+26
-44
@@ -1727,7 +1727,7 @@ export namespace Browser {
|
||||
*
|
||||
* `allow`: Allow sites to use advanced clipboard capabilities,
|
||||
*
|
||||
* `block`: Don't allow sites to use advanced clipboard capabilties,
|
||||
* `block`: Don't allow sites to use advanced clipboard capabilities,
|
||||
*
|
||||
* `ask`: Ask when a site wants to use advanced clipboard capabilities.
|
||||
*
|
||||
@@ -1878,6 +1878,8 @@ export namespace Browser {
|
||||
BROWSER_ACTION = "browser_action",
|
||||
PAGE_ACTION = "page_action",
|
||||
ACTION = "action",
|
||||
/** @since Chrome 149 */
|
||||
TAB = "tab",
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2248,7 +2250,7 @@ export namespace Browser {
|
||||
*
|
||||
* Permissions: "debugger"
|
||||
*/
|
||||
export namespace _debugger {
|
||||
namespace _debugger {
|
||||
/** Debuggee identifier. Either tabId, extensionId or targetId must be specified */
|
||||
interface Debuggee {
|
||||
/** The id of the tab which you intend to debug. */
|
||||
@@ -2587,7 +2589,7 @@ export namespace Browser {
|
||||
interface EditResponseCookie {
|
||||
/** Filter for cookies that will be modified. All empty entries are ignored. */
|
||||
filter: ResponseCookie;
|
||||
/** Attributes that shall be overridden in cookies that machted the filter. Attributes that are set to an empty string are removed. */
|
||||
/** Attributes that shall be overridden in cookies that matched the filter. Attributes that are set to an empty string are removed. */
|
||||
modification: ResponseCookie;
|
||||
}
|
||||
|
||||
@@ -2605,7 +2607,7 @@ export namespace Browser {
|
||||
interface EditRequestCookie {
|
||||
/** Filter for cookies that will be modified. All empty entries are ignored. */
|
||||
filter: RequestCookie;
|
||||
/** Attributes that shall be overridden in cookies that machted the filter. Attributes that are set to an empty string are removed. */
|
||||
/** Attributes that shall be overridden in cookies that matched the filter. Attributes that are set to an empty string are removed. */
|
||||
modification: RequestCookie;
|
||||
}
|
||||
|
||||
@@ -4547,7 +4549,7 @@ export namespace Browser {
|
||||
const inIncognitoContext: boolean;
|
||||
|
||||
/**
|
||||
* Set for the lifetime of a callback if an ansychronous extension api has resulted in an error. If no error has occurred lastError will be `undefined`.
|
||||
* Set for the lifetime of a callback if an asynchronous extension api has resulted in an error. If no error has occurred lastError will be `undefined`.
|
||||
* @deprecated since Chrome 58. Please use {@link runtime.lastError}
|
||||
*/
|
||||
const lastError: runtime.LastError | undefined;
|
||||
@@ -8914,6 +8916,7 @@ export namespace Browser {
|
||||
/**
|
||||
* The native client architecture. This may be different from arch on some platforms.
|
||||
* @since Chrome 44
|
||||
* @deprecated since Chrome 149. This enum is deprecated following complete removal of Native Client.
|
||||
*/
|
||||
enum PlatformNaclArch {
|
||||
/** Specifies the native client architecture as arm. */
|
||||
@@ -9074,7 +9077,10 @@ export namespace Browser {
|
||||
os: `${PlatformOs}`;
|
||||
/** The machine's processor architecture. */
|
||||
arch: `${PlatformArch}`;
|
||||
/** The native client architecture. This may be different from arch on some platforms. */
|
||||
/**
|
||||
* The native client architecture. This may be different from arch on some platforms.
|
||||
* @deprecated since Chrome 149. This attribute is deprecated following complete removal of Native Client.
|
||||
*/
|
||||
nacl_arch?: `${PlatformNaclArch}`;
|
||||
}
|
||||
|
||||
@@ -9219,11 +9225,6 @@ export namespace Browser {
|
||||
| "webRequestBlocking"
|
||||
| "webRequestAuthProvider";
|
||||
|
||||
/**
|
||||
* @deprecated Use `ManifestPermission` instead.
|
||||
*/
|
||||
type ManifestPermissions = ManifestPermission;
|
||||
|
||||
/** Source : https://developer.chrome.com/docs/extensions/reference/api/permissions */
|
||||
type ManifestOptionalPermission = Exclude<
|
||||
ManifestPermission,
|
||||
@@ -9241,11 +9242,6 @@ export namespace Browser {
|
||||
| "webAuthenticationProxy"
|
||||
>;
|
||||
|
||||
/**
|
||||
* @deprecated Use `ManifestOptionalPermission` instead.
|
||||
*/
|
||||
type ManifestOptionalPermissions = ManifestOptionalPermission;
|
||||
|
||||
interface SearchProvider {
|
||||
name?: string | undefined;
|
||||
keyword?: string | undefined;
|
||||
@@ -10114,12 +10110,7 @@ export namespace Browser {
|
||||
* Permissions: "storage"
|
||||
*/
|
||||
export namespace storage {
|
||||
/** NoInfer for old TypeScript versions (Required TS 5.4+) */
|
||||
type NoInferX<T> = T[][T extends any ? 0 : never];
|
||||
// The next line prevents things without the export keyword from being automatically exported (like NoInferX)
|
||||
export {};
|
||||
|
||||
export interface StorageArea {
|
||||
interface StorageArea {
|
||||
/**
|
||||
* Gets the amount of space (in bytes) being used by one or more items.
|
||||
* @param keys A single key or list of keys to get the total usage for. An empty list will return 0. Pass in `null` to get the total usage of all of storage.
|
||||
@@ -10166,11 +10157,11 @@ export namespace Browser {
|
||||
* Can return its result via Promise in Manifest V3 or later since Chrome 95.
|
||||
*/
|
||||
get<T = { [key: string]: unknown }>(
|
||||
keys?: NoInferX<keyof T> | Array<NoInferX<keyof T>> | Partial<NoInferX<T>> | null,
|
||||
keys?: NoInfer<keyof T> | Array<NoInfer<keyof T>> | Partial<NoInfer<T>> | null | undefined,
|
||||
): Promise<T>;
|
||||
get<T = { [key: string]: unknown }>(callback: (items: T) => void): void;
|
||||
get<T = { [key: string]: unknown }>(
|
||||
keys: NoInferX<keyof T> | Array<NoInferX<keyof T>> | Partial<NoInferX<T>> | null | undefined,
|
||||
keys: NoInfer<keyof T> | Array<NoInfer<keyof T>> | Partial<NoInfer<T>> | null | undefined,
|
||||
callback: (items: T) => void,
|
||||
): void;
|
||||
|
||||
@@ -10197,19 +10188,19 @@ export namespace Browser {
|
||||
getKeys(callback: (keys: string[]) => void): void;
|
||||
}
|
||||
|
||||
export interface StorageChange {
|
||||
interface StorageChange {
|
||||
/** The new value of the item, if there is a new value. */
|
||||
newValue?: unknown;
|
||||
/** The old value of the item, if there was an old value. */
|
||||
oldValue?: unknown;
|
||||
}
|
||||
|
||||
export interface LocalStorageArea extends StorageArea {
|
||||
interface LocalStorageArea extends StorageArea {
|
||||
/** The maximum amount (in bytes) of data that can be stored in local storage, as measured by the JSON stringification of every value plus every key's length. This value will be ignored if the extension has the unlimitedStorage permission. Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError when using a callback, or a rejected Promise if using async/await. */
|
||||
QUOTA_BYTES: 10485760;
|
||||
}
|
||||
|
||||
export interface SyncStorageArea extends StorageArea {
|
||||
interface SyncStorageArea extends StorageArea {
|
||||
/** @deprecated The storage.sync API no longer has a sustained write operation quota. */
|
||||
MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE: 1000000;
|
||||
/** The maximum total amount (in bytes) of data that can be stored in sync storage, as measured by the JSON stringification of every value plus every key's length. Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError when using a callback, or when a Promise is rejected. */
|
||||
@@ -10232,18 +10223,18 @@ export namespace Browser {
|
||||
MAX_WRITE_OPERATIONS_PER_MINUTE: 120;
|
||||
}
|
||||
|
||||
export interface SessionStorageArea extends StorageArea {
|
||||
interface SessionStorageArea extends StorageArea {
|
||||
/** The maximum amount (in bytes) of data that can be stored in memory, as measured by estimating the dynamically allocated memory usage of every value and key. Updates that would cause this limit to be exceeded fail immediately and set runtime.lastError when using a callback, or when a Promise is rejected. */
|
||||
QUOTA_BYTES: 10485760;
|
||||
}
|
||||
|
||||
export type AreaName = "sync" | "local" | "managed" | "session";
|
||||
type AreaName = "sync" | "local" | "managed" | "session";
|
||||
|
||||
/**
|
||||
* The storage area's access level.
|
||||
* @since Chrome 102
|
||||
*/
|
||||
export enum AccessLevel {
|
||||
enum AccessLevel {
|
||||
/** Specifies contexts originating from the extension itself. */
|
||||
TRUSTED_CONTEXTS = "TRUSTED_CONTEXTS",
|
||||
/** Specifies contexts originating from outside the extension. */
|
||||
@@ -10251,13 +10242,13 @@ export namespace Browser {
|
||||
}
|
||||
|
||||
/** Items in the `local` storage area are local to each machine. */
|
||||
export const local: LocalStorageArea;
|
||||
const local: LocalStorageArea;
|
||||
|
||||
/** Items in the `sync` storage area are synced using Chrome Sync. */
|
||||
export const sync: SyncStorageArea;
|
||||
const sync: SyncStorageArea;
|
||||
|
||||
/** Items in the `managed` storage area are set by an enterprise policy configured by the domain administrator, and are read-only for the extension; trying to modify this namespace results in an error. For information on configuring a policy, see Manifest for storage areas. */
|
||||
export const managed: StorageArea;
|
||||
const managed: StorageArea;
|
||||
|
||||
/**
|
||||
* Items in the `session` storage area are stored in-memory and will not be persisted to disk.
|
||||
@@ -10265,10 +10256,10 @@ export namespace Browser {
|
||||
* MV3 only
|
||||
* @since Chrome 102
|
||||
*/
|
||||
export const session: SessionStorageArea;
|
||||
const session: SessionStorageArea;
|
||||
|
||||
/** Fired when one or more items change. */
|
||||
export const onChanged: events.Event<(changes: { [key: string]: StorageChange }, areaName: AreaName) => void>;
|
||||
const onChanged: events.Event<(changes: { [key: string]: StorageChange }, areaName: AreaName) => void>;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
@@ -10291,10 +10282,6 @@ export namespace Browser {
|
||||
total: number;
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link CpuTime} instead. */
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface ProcessorUsage extends CpuTime {}
|
||||
|
||||
interface ProcessorInfo {
|
||||
/** Cumulative usage info for this logical processor. */
|
||||
usage: CpuTime;
|
||||
@@ -12527,10 +12514,6 @@ export namespace Browser {
|
||||
reconnect?: string | undefined;
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link Parameters} instead */
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface VpnSessionParameters extends Parameters {}
|
||||
|
||||
/** The enum is used by the platform to notify the client of the VPN session status. */
|
||||
enum PlatformMessage {
|
||||
/** Indicates that the VPN configuration connected. */
|
||||
@@ -13435,7 +13418,6 @@ export namespace Browser {
|
||||
|
||||
/** Fired before sending an HTTP request, once the request headers are available. This may occur after a TCP connection is made to the server, but before any HTTP data is sent. */
|
||||
const onBeforeSendHeaders: WebRequestEvent<
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
(details: OnBeforeSendHeadersDetails) => BlockingResponse | undefined,
|
||||
`${OnBeforeSendHeadersOptions}`[]
|
||||
>;
|
||||
|
||||
@@ -173,6 +173,23 @@ dollars: $$$1
|
||||
i18n.t('dollars', ['1.00']); // "$1.00"
|
||||
```
|
||||
|
||||
#### Named Substitutions
|
||||
|
||||
You can also use named substitutions with `{key}` placeholders:
|
||||
|
||||
```yml
|
||||
welcome: Hello {name}, welcome to {appName}!
|
||||
```
|
||||
|
||||
```ts
|
||||
i18n.t('welcome', {
|
||||
name: 'Ada',
|
||||
appName: 'WXT',
|
||||
}); // "Hello Ada, welcome to WXT!"
|
||||
```
|
||||
|
||||
Missing keys are left unchanged in the returned message.
|
||||
|
||||
### Plural Forms
|
||||
|
||||
> [!WARNING]
|
||||
|
||||
@@ -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,20 @@
|
||||
{
|
||||
"simple": {
|
||||
"message": "Just plain text"
|
||||
},
|
||||
"substitution": {
|
||||
"message": "Includes one substitution: $1"
|
||||
},
|
||||
"namedSubstitution": {
|
||||
"message": "Includes one named substitution: {value}"
|
||||
},
|
||||
"singular": {
|
||||
"message": "There is $1 item"
|
||||
},
|
||||
"plural": {
|
||||
"message": "There are $1 items"
|
||||
},
|
||||
"wxtPlural": {
|
||||
"message": "There is $1 item | There are $1 items"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
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 key = 'namedSubstitution';
|
||||
const value = 'test';
|
||||
const namedSubstitution = new Bench({ name: 'Named substitution' })
|
||||
.add('Vanilla', () => void chrome.i18n.getMessage(key))
|
||||
.add('WXT I18n', () => void i18n.t(key, { value }));
|
||||
await runBench(namedSubstitution);
|
||||
}
|
||||
|
||||
{
|
||||
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...');
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
},
|
||||
});
|
||||
@@ -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"
|
||||
@@ -46,6 +47,7 @@
|
||||
"@types/node": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"tinybench": "^6.0.1",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
|
||||
@@ -22,6 +22,7 @@ describe('Built Tools', () => {
|
||||
const fileText = stringifyYAML({
|
||||
simple: 'example',
|
||||
sub: 'Hello $1',
|
||||
named: 'Hello {name} from {tool}',
|
||||
nested: {
|
||||
example: 'This is nested',
|
||||
array: ['One', 'Two'],
|
||||
@@ -33,11 +34,19 @@ describe('Built Tools', () => {
|
||||
message: 'test 2',
|
||||
description: 'test',
|
||||
},
|
||||
chromeNamed: {
|
||||
message: 'test {value}',
|
||||
description: 'test',
|
||||
},
|
||||
plural0: {
|
||||
0: 'Zero items',
|
||||
1: 'One item',
|
||||
n: '$1 items',
|
||||
},
|
||||
pluralNamed: {
|
||||
1: 'One {item}',
|
||||
n: '$1 {item}',
|
||||
},
|
||||
plural1: {
|
||||
1: 'One item',
|
||||
n: '$1 items',
|
||||
@@ -68,6 +77,9 @@ describe('Built Tools', () => {
|
||||
"sub": {
|
||||
"message": "Hello $1"
|
||||
},
|
||||
"named": {
|
||||
"message": "Hello {name} from {tool}"
|
||||
},
|
||||
"nested_example": {
|
||||
"message": "This is nested"
|
||||
},
|
||||
@@ -84,9 +96,16 @@ describe('Built Tools', () => {
|
||||
"message": "test 2",
|
||||
"description": "test"
|
||||
},
|
||||
"chromeNamed": {
|
||||
"message": "test {value}",
|
||||
"description": "test"
|
||||
},
|
||||
"plural0": {
|
||||
"message": "Zero items | One item | $1 items"
|
||||
},
|
||||
"pluralNamed": {
|
||||
"message": "One {item} | $1 {item}"
|
||||
},
|
||||
"plural1": {
|
||||
"message": "One item | $1 items"
|
||||
},
|
||||
@@ -103,12 +122,15 @@ describe('Built Tools', () => {
|
||||
"export type GeneratedI18nStructure = {
|
||||
"simple": { substitutions: 0, plural: false };
|
||||
"sub": { substitutions: 1, plural: false };
|
||||
"named": { substitutions: 0, plural: false, namedSubstitutions: ["name","tool"] };
|
||||
"nested.example": { substitutions: 0, plural: false };
|
||||
"nested.array.0": { substitutions: 0, plural: false };
|
||||
"nested.array.1": { substitutions: 0, plural: false };
|
||||
"nested.notChrome.message": { substitutions: 0, plural: false };
|
||||
"chrome": { substitutions: 0, plural: false };
|
||||
"chromeNamed": { substitutions: 0, plural: false, namedSubstitutions: ["value"] };
|
||||
"plural0": { substitutions: 1, plural: true };
|
||||
"pluralNamed": { substitutions: 1, plural: true, namedSubstitutions: ["item"] };
|
||||
"plural1": { substitutions: 1, plural: true };
|
||||
"pluralN": { substitutions: 1, plural: true };
|
||||
"pluralSub": { substitutions: 2, plural: true };
|
||||
|
||||
@@ -68,4 +68,47 @@ describe('createI18n', () => {
|
||||
i18n.t('key', 3, ['custom']);
|
||||
expect(getMessageMock).toBeCalledWith('key', ['custom']);
|
||||
});
|
||||
|
||||
it('should replace named substitutions', () => {
|
||||
const i18n = createI18n();
|
||||
getMessageMock.mockReturnValue('My name is {name}, and I use {tool}.');
|
||||
|
||||
const actual = i18n.t('key', {
|
||||
name: 'Ada',
|
||||
tool: 'WXT',
|
||||
});
|
||||
|
||||
expect(actual).toBe('My name is Ada, and I use WXT.');
|
||||
expect(getMessageMock).toBeCalledTimes(1);
|
||||
expect(getMessageMock).toBeCalledWith('key');
|
||||
});
|
||||
|
||||
it('should leave unknown named substitutions unchanged', () => {
|
||||
const i18n = createI18n();
|
||||
getMessageMock.mockReturnValue('Hello {name} from {team}.');
|
||||
|
||||
const actual = i18n.t('key', { name: 'Ada' });
|
||||
|
||||
expect(actual).toBe('Hello Ada from {team}.');
|
||||
});
|
||||
|
||||
it('should combine positional and named substitutions', () => {
|
||||
const i18n = createI18n();
|
||||
getMessageMock.mockReturnValue('Hello Ada from {team}.');
|
||||
|
||||
const actual = i18n.t('key', ['Ada'], { team: 'WXT' });
|
||||
|
||||
expect(actual).toBe('Hello Ada from WXT.');
|
||||
expect(getMessageMock).toBeCalledWith('key', ['Ada']);
|
||||
});
|
||||
|
||||
it('should replace named substitutions after pluralization', () => {
|
||||
const i18n = createI18n();
|
||||
getMessageMock.mockReturnValue('One {item} | Multiple {item}');
|
||||
|
||||
const actual = i18n.t('key', 2, { item: 'files' });
|
||||
|
||||
expect(actual).toBe('Multiple files');
|
||||
expect(getMessageMock).toBeCalledWith('key', ['2']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,9 +39,29 @@ describe('I18n Types', () => {
|
||||
simple: { plural: false; substitutions: 0 };
|
||||
simpleSub1: { plural: false; substitutions: 1 };
|
||||
simpleSub2: { plural: false; substitutions: 2 };
|
||||
named: {
|
||||
plural: false;
|
||||
substitutions: 0;
|
||||
namedSubstitutions: ['name', 'tool'];
|
||||
};
|
||||
mixed: {
|
||||
plural: false;
|
||||
substitutions: 1;
|
||||
namedSubstitutions: ['team'];
|
||||
};
|
||||
plural: { plural: true; substitutions: 0 };
|
||||
pluralSub1: { plural: true; substitutions: 1 };
|
||||
pluralSub2: { plural: true; substitutions: 2 };
|
||||
pluralNamed: {
|
||||
plural: true;
|
||||
substitutions: 1;
|
||||
namedSubstitutions: ['item'];
|
||||
};
|
||||
pluralMixed: {
|
||||
plural: true;
|
||||
substitutions: 2;
|
||||
namedSubstitutions: ['item'];
|
||||
};
|
||||
};
|
||||
|
||||
const i18n = createI18n<MyStructure>();
|
||||
@@ -76,6 +96,22 @@ describe('I18n Types', () => {
|
||||
// @ts-expect-error
|
||||
i18n.t('simpleSub2', n);
|
||||
|
||||
i18n.t('named', { name: 'Ada', tool: 'WXT' });
|
||||
// @ts-expect-error
|
||||
i18n.t('named');
|
||||
// @ts-expect-error
|
||||
i18n.t('named', { name: 'Ada' });
|
||||
// @ts-expect-error
|
||||
i18n.t('named', ['Ada', 'WXT']);
|
||||
|
||||
i18n.t('mixed', ['Ada'], { team: 'WXT' });
|
||||
// @ts-expect-error
|
||||
i18n.t('mixed', { team: 'WXT' });
|
||||
// @ts-expect-error
|
||||
i18n.t('mixed', ['Ada']);
|
||||
// @ts-expect-error
|
||||
i18n.t('mixed', ['Ada'], {});
|
||||
|
||||
i18n.t('plural', n);
|
||||
// @ts-expect-error
|
||||
i18n.t('plural');
|
||||
@@ -109,6 +145,25 @@ describe('I18n Types', () => {
|
||||
i18n.t('pluralSub2', n, ['one', 'two', 'three']);
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralSub2', n);
|
||||
|
||||
i18n.t('pluralNamed', n, { item: 'files' });
|
||||
i18n.t('pluralNamed', n, ['files'], { item: 'files' });
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralNamed', n);
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralNamed', n, {});
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralNamed', n, ['files']);
|
||||
|
||||
i18n.t('pluralMixed', n, ['1', 'files'], { item: 'reports' });
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralMixed', n, { item: 'reports' });
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralMixed', n, ['1', 'files']);
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralMixed', n, ['1'], { item: 'reports' });
|
||||
// @ts-expect-error
|
||||
i18n.t('pluralMixed', n, ['1', 'files'], {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ChromeMessage } from '../build';
|
||||
import {
|
||||
applyNamedSubstitutions,
|
||||
applyChromeMessagePlaceholders,
|
||||
getNamedSubstitutionNames,
|
||||
getSubstitutionCount,
|
||||
standardizeLocale,
|
||||
} from '../utils';
|
||||
@@ -42,6 +44,34 @@ describe('Utils', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyNamedSubstitutions', () => {
|
||||
it('should replace named substitutions', () => {
|
||||
expect(
|
||||
applyNamedSubstitutions('Hello {name}, welcome to {tool}', {
|
||||
name: 'Ada',
|
||||
tool: 'WXT',
|
||||
}),
|
||||
).toBe('Hello Ada, welcome to WXT');
|
||||
});
|
||||
|
||||
it('should leave missing substitutions unchanged', () => {
|
||||
expect(applyNamedSubstitutions('Hello {name}', {})).toBe('Hello {name}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNamedSubstitutionNames', () => {
|
||||
it('should return unique substitution names in order', () => {
|
||||
expect(getNamedSubstitutionNames('{name} uses {tool}: {name}')).toEqual([
|
||||
'name',
|
||||
'tool',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array when no named substitutions are present', () => {
|
||||
expect(getNamedSubstitutionNames('Hello $1')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSubstitutionCount', () => {
|
||||
it('should return the last substitution present in the message', () => {
|
||||
expect(getSubstitutionCount('I like $1, but I like $2 better')).toBe(2);
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { parseYAML, parseJSON5, parseTOML } from 'confbox';
|
||||
import { dirname, extname } from 'node:path';
|
||||
import { applyChromeMessagePlaceholders, getSubstitutionCount } from './utils';
|
||||
import {
|
||||
applyChromeMessagePlaceholders,
|
||||
getNamedSubstitutionNames,
|
||||
getSubstitutionCount,
|
||||
} from './utils';
|
||||
|
||||
export { SUPPORTED_LOCALES } from './supported-locales';
|
||||
|
||||
@@ -38,6 +42,7 @@ export type MessagesObject = Record<string, Message>;
|
||||
export interface ParsedBaseMessage {
|
||||
key: string[];
|
||||
substitutions: number;
|
||||
namedSubstitutions: string[];
|
||||
}
|
||||
|
||||
export interface ParsedChromeMessage extends ParsedBaseMessage, ChromeMessage {
|
||||
@@ -167,11 +172,13 @@ function _parseMessagesObject(
|
||||
case 'symbol': {
|
||||
const message = String(object);
|
||||
const substitutions = getSubstitutionCount(message);
|
||||
const namedSubstitutions = getNamedSubstitutionNames(message);
|
||||
return [
|
||||
{
|
||||
type: 'simple',
|
||||
key: path,
|
||||
substitutions,
|
||||
namedSubstitutions,
|
||||
message,
|
||||
},
|
||||
];
|
||||
@@ -191,11 +198,13 @@ function _parseMessagesObject(
|
||||
if (isPluralMessage(object)) {
|
||||
const message = Object.values(object).join('|');
|
||||
const substitutions = getSubstitutionCount(message);
|
||||
const namedSubstitutions = getNamedSubstitutionNames(message);
|
||||
return [
|
||||
{
|
||||
type: 'plural',
|
||||
key: path,
|
||||
substitutions,
|
||||
namedSubstitutions,
|
||||
plurals: object,
|
||||
},
|
||||
];
|
||||
@@ -204,11 +213,13 @@ function _parseMessagesObject(
|
||||
if (depth === 1 && isChromeMessage(object)) {
|
||||
const message = applyChromeMessagePlaceholders(object);
|
||||
const substitutions = getSubstitutionCount(message);
|
||||
const namedSubstitutions = getNamedSubstitutionNames(message);
|
||||
return [
|
||||
{
|
||||
type: 'chrome',
|
||||
key: path,
|
||||
substitutions,
|
||||
namedSubstitutions,
|
||||
...object,
|
||||
},
|
||||
];
|
||||
@@ -247,6 +258,11 @@ export function generateTypeText(messages: ParsedMessage[]): string {
|
||||
`substitutions: ${message.substitutions}`,
|
||||
`plural: ${message.type === 'plural'}`,
|
||||
];
|
||||
if (message.namedSubstitutions.length > 0) {
|
||||
features.push(
|
||||
`namedSubstitutions: ${JSON.stringify(message.namedSubstitutions)}`,
|
||||
);
|
||||
}
|
||||
return ` "${key}": { ${features.join(', ')} };`;
|
||||
};
|
||||
|
||||
|
||||
+39
-17
@@ -1,6 +1,13 @@
|
||||
/** @module @wxt-dev/i18n */
|
||||
import { I18nStructure, I18n, Substitution, UntypedI18n } from './types';
|
||||
import type {
|
||||
I18nStructure,
|
||||
I18n,
|
||||
NamedSubstitutions,
|
||||
Substitution,
|
||||
UntypedI18n,
|
||||
} from './types';
|
||||
import { browser } from '@wxt-dev/browser';
|
||||
import { applyNamedSubstitutions } from './utils';
|
||||
|
||||
export function createI18n(): UntypedI18n;
|
||||
export function createI18n<T extends I18nStructure>(): I18n<T>;
|
||||
@@ -8,6 +15,7 @@ export function createI18n(): UntypedI18n {
|
||||
const t: UntypedI18n['t'] = (key: string, ...args: unknown[]) => {
|
||||
// Resolve args
|
||||
let sub: Substitution[] | undefined;
|
||||
let namedSub: NamedSubstitutions | undefined;
|
||||
let count: number | undefined;
|
||||
args.forEach((arg, i) => {
|
||||
if (arg == null) {
|
||||
@@ -16,9 +24,11 @@ export function createI18n(): UntypedI18n {
|
||||
count = arg;
|
||||
} else if (Array.isArray(arg)) {
|
||||
sub = arg;
|
||||
} else if (isNamedSubstitutions(arg)) {
|
||||
namedSub = arg;
|
||||
} else {
|
||||
throw Error(
|
||||
`Unknown argument at index ${i}. Must be a number for pluralization, substitution array, or options object.`,
|
||||
`Unknown argument at index ${i}. Must be a number for pluralization, substitution array, or named substitution object.`,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -40,24 +50,36 @@ export function createI18n(): UntypedI18n {
|
||||
if (!message) {
|
||||
console.warn(`[i18n] Message not found: "${key}"`);
|
||||
}
|
||||
if (count == null) return message;
|
||||
|
||||
// Apply pluralization
|
||||
const plural = message.split(' | ');
|
||||
switch (plural.length) {
|
||||
// "n items"
|
||||
case 1:
|
||||
return plural[0];
|
||||
// "1 item | n items"
|
||||
case 2:
|
||||
return plural[count === 1 ? 0 : 1];
|
||||
// "0 items | 1 item | n items"
|
||||
case 3:
|
||||
return plural[count === 0 || count === 1 ? count : 2];
|
||||
default:
|
||||
throw Error('Unknown plural formatting');
|
||||
if (count != null) {
|
||||
// Apply pluralization
|
||||
const plural = message.split(' | ');
|
||||
switch (plural.length) {
|
||||
// "n items"
|
||||
case 1:
|
||||
message = plural[0];
|
||||
break;
|
||||
// "1 item | n items"
|
||||
case 2:
|
||||
message = plural[count === 1 ? 0 : 1];
|
||||
break;
|
||||
// "0 items | 1 item | n items"
|
||||
case 3:
|
||||
message = plural[count === 0 || count === 1 ? count : 2];
|
||||
break;
|
||||
default:
|
||||
throw Error('Unknown plural formatting');
|
||||
}
|
||||
}
|
||||
|
||||
return namedSub == null
|
||||
? message
|
||||
: applyNamedSubstitutions(message, namedSub);
|
||||
};
|
||||
|
||||
return { t };
|
||||
}
|
||||
|
||||
function isNamedSubstitutions(value: unknown): value is NamedSubstitutions {
|
||||
return typeof value === 'object' && value != null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export interface I18nFeatures {
|
||||
plural: boolean;
|
||||
substitutions: SubstitutionCount;
|
||||
namedSubstitutions?: readonly string[];
|
||||
}
|
||||
|
||||
export interface UntypedI18n {
|
||||
@@ -10,8 +11,21 @@ export interface UntypedI18n {
|
||||
export type UntypedTFunction = {
|
||||
(key: string): string;
|
||||
(key: string, substitutions: Substitution[]): string;
|
||||
(key: string, substitutions: NamedSubstitutions): string;
|
||||
(
|
||||
key: string,
|
||||
substitutions: Substitution[],
|
||||
namedSubstitutions: NamedSubstitutions,
|
||||
): string;
|
||||
(key: string, n: number): string;
|
||||
(key: string, n: number, substitutions: Substitution[]): string;
|
||||
(key: string, n: number, substitutions: NamedSubstitutions): string;
|
||||
(
|
||||
key: string,
|
||||
n: number,
|
||||
substitutions: Substitution[],
|
||||
namedSubstitutions: NamedSubstitutions,
|
||||
): string;
|
||||
};
|
||||
|
||||
export type I18nStructure = Record<string, I18nFeatures>;
|
||||
@@ -20,6 +34,12 @@ export interface I18n<T extends I18nStructure> {
|
||||
t: TFunction<T>;
|
||||
}
|
||||
|
||||
type HasNamedSubstitutions<T extends I18nFeatures> = T extends {
|
||||
namedSubstitutions: readonly string[];
|
||||
}
|
||||
? true
|
||||
: false;
|
||||
|
||||
// prettier-ignore
|
||||
export type SubstitutionTuple<T extends SubstitutionCount> =
|
||||
T extends 1 ? [$1: Substitution]
|
||||
@@ -37,22 +57,43 @@ export type TFunction<T extends I18nStructure> = {
|
||||
// Non-plural, no substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: false; substitutions: 0 } ? P : never; }[keyof T],
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: false; substitutions: 0 } ? HasNamedSubstitutions<T[P]> extends true ? never : P : never; }[keyof T],
|
||||
): string;
|
||||
|
||||
// Non-plural with substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: false; substitutions: SubstitutionCount } ? P : never; }[keyof T],
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: false; substitutions: SubstitutionCount } ? HasNamedSubstitutions<T[P]> extends true ? never : P : never; }[keyof T],
|
||||
substitutions: T[K] extends I18nFeatures
|
||||
? SubstitutionTuple<T[K]['substitutions']>
|
||||
: never,
|
||||
): string;
|
||||
|
||||
// Non-plural with named substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: false; substitutions: 0; namedSubstitutions: readonly string[] } ? P : never; }[keyof T],
|
||||
substitutions: T[K] extends I18nFeatures
|
||||
? NamedSubstitutionsFor<T[K]>
|
||||
: never,
|
||||
): string;
|
||||
|
||||
// Non-plural with positional and named substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: false; substitutions: PositiveSubstitutionCount; namedSubstitutions: readonly string[] } ? P : never; }[keyof T],
|
||||
substitutions: T[K] extends I18nFeatures
|
||||
? SubstitutionTuple<T[K]['substitutions']>
|
||||
: never,
|
||||
namedSubstitutions: T[K] extends I18nFeatures
|
||||
? NamedSubstitutionsFor<T[K]>
|
||||
: never,
|
||||
): string;
|
||||
|
||||
// Plural with 1 substitution
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: 1 } ? P : never; }[keyof T],
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: 1 } ? HasNamedSubstitutions<T[P]> extends true ? never : P : never; }[keyof T],
|
||||
n: number,
|
||||
substitutions?: SubstitutionTuple<1>,
|
||||
): string;
|
||||
@@ -60,21 +101,52 @@ export type TFunction<T extends I18nStructure> = {
|
||||
// Plural without substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: 0 | 1 } ? P : never; }[keyof T],
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: 0 | 1 } ? HasNamedSubstitutions<T[P]> extends true ? never : P : never; }[keyof T],
|
||||
n: number,
|
||||
): string;
|
||||
|
||||
// Plural with substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: SubstitutionCount } ? P : never; }[keyof T],
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: SubstitutionCount } ? HasNamedSubstitutions<T[P]> extends true ? never : P : never; }[keyof T],
|
||||
n: number,
|
||||
substitutions: T[K] extends I18nFeatures
|
||||
? SubstitutionTuple<T[K]['substitutions']>
|
||||
: never,
|
||||
): string;
|
||||
|
||||
// Plural with named substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: 0 | 1; namedSubstitutions: readonly string[] } ? P : never; }[keyof T],
|
||||
n: number,
|
||||
substitutions: T[K] extends I18nFeatures
|
||||
? NamedSubstitutionsFor<T[K]>
|
||||
: never,
|
||||
): string;
|
||||
|
||||
// Plural with positional and named substitutions
|
||||
<K extends keyof T>(
|
||||
// prettier-ignore
|
||||
key: K & { [P in keyof T]: T[P] extends { plural: true; substitutions: PositiveSubstitutionCount; namedSubstitutions: readonly string[] } ? P : never; }[keyof T],
|
||||
n: number,
|
||||
substitutions: T[K] extends I18nFeatures
|
||||
? SubstitutionTuple<T[K]['substitutions']>
|
||||
: never,
|
||||
namedSubstitutions: T[K] extends I18nFeatures
|
||||
? NamedSubstitutionsFor<T[K]>
|
||||
: never,
|
||||
): string;
|
||||
};
|
||||
|
||||
export type Substitution = string | number;
|
||||
export type NamedSubstitutions = Record<string, Substitution>;
|
||||
|
||||
type SubstitutionCount = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
||||
type PositiveSubstitutionCount = Exclude<SubstitutionCount, 0>;
|
||||
|
||||
type NamedSubstitutionsFor<T extends I18nFeatures> = T extends {
|
||||
namedSubstitutions: readonly string[];
|
||||
}
|
||||
? { [K in T['namedSubstitutions'][number]]: Substitution }
|
||||
: never;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { ChromeMessage } from './build';
|
||||
import type { ChromeMessage } from './build';
|
||||
import type { NamedSubstitutions } from './types';
|
||||
|
||||
const NAMED_SUBSTITUTION_RE = /\{([A-Za-z0-9_]+)\}/g;
|
||||
|
||||
export function applyChromeMessagePlaceholders(message: ChromeMessage): string {
|
||||
if (message.placeholders == null) return message.message;
|
||||
@@ -11,6 +14,24 @@ export function applyChromeMessagePlaceholders(message: ChromeMessage): string {
|
||||
);
|
||||
}
|
||||
|
||||
export function applyNamedSubstitutions(
|
||||
message: string,
|
||||
substitutions: NamedSubstitutions,
|
||||
): string {
|
||||
return message.replace(NAMED_SUBSTITUTION_RE, (match, key: string) => {
|
||||
return Object.prototype.hasOwnProperty.call(substitutions, key)
|
||||
? String(substitutions[key])
|
||||
: match;
|
||||
});
|
||||
}
|
||||
|
||||
export function getNamedSubstitutionNames(message: string): string[] {
|
||||
return Array.from(
|
||||
message.matchAll(NAMED_SUBSTITUTION_RE),
|
||||
([, name]) => name,
|
||||
).filter((name, i, names) => names.indexOf(name) === i);
|
||||
}
|
||||
|
||||
export function getSubstitutionCount(message: string): number {
|
||||
return (
|
||||
1 +
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
"types": ["bun"]
|
||||
},
|
||||
"exclude": ["node_modules/**", "dist/**"]
|
||||
}
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"defu": "^6.1.4",
|
||||
"tinyglobby": "^0.2.15"
|
||||
"tinyglobby": "^0.2.16"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,15 @@ export default defineContentScript({
|
||||
append: 'after',
|
||||
anchor: '#automount-anchor',
|
||||
onMount: (container) => {
|
||||
container.classList.add(
|
||||
'flex',
|
||||
'-ml-9',
|
||||
'items-start',
|
||||
'justify-start',
|
||||
);
|
||||
const app = document.createElement('div');
|
||||
app.id = 'automount-ui';
|
||||
app.classList.add('m-0', 'text-center', 'text-blue-500');
|
||||
app.classList.add('mt-9', 'text-center', 'text-blue-500');
|
||||
app.textContent = `Hello, I'm automount UI.`;
|
||||
container.append(app);
|
||||
},
|
||||
|
||||
@@ -1,5 +1,61 @@
|
||||
# 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)
|
||||
|
||||
@@ -6,6 +6,7 @@ const hooks: WxtHooks = {
|
||||
ready: vi.fn(),
|
||||
'config:resolved': vi.fn(),
|
||||
'prepare:types': vi.fn(),
|
||||
'prepare:tsconfig': vi.fn(),
|
||||
'prepare:publicPaths': vi.fn(),
|
||||
'build:before': vi.fn(),
|
||||
'build:done': vi.fn(),
|
||||
@@ -56,6 +57,7 @@ describe('Hooks', () => {
|
||||
ready: true,
|
||||
'config:resolved': true,
|
||||
'prepare:types': true,
|
||||
'prepare:tsconfig': true,
|
||||
'prepare:publicPaths': true,
|
||||
'build:before': false,
|
||||
'build:done': false,
|
||||
@@ -88,6 +90,7 @@ describe('Hooks', () => {
|
||||
ready: true,
|
||||
'config:resolved': true,
|
||||
'prepare:types': true,
|
||||
'prepare:tsconfig': true,
|
||||
'prepare:publicPaths': true,
|
||||
'build:before': true,
|
||||
'build:done': true,
|
||||
@@ -120,6 +123,7 @@ describe('Hooks', () => {
|
||||
ready: true,
|
||||
'config:resolved': true,
|
||||
'prepare:types': true,
|
||||
'prepare:tsconfig': true,
|
||||
'prepare:publicPaths': true,
|
||||
'build:before': true,
|
||||
'build:done': true,
|
||||
@@ -152,6 +156,7 @@ describe('Hooks', () => {
|
||||
ready: true,
|
||||
'config:resolved': true,
|
||||
'prepare:types': true,
|
||||
'prepare:tsconfig': true,
|
||||
'prepare:publicPaths': true,
|
||||
'build:before': true,
|
||||
'build:done': true,
|
||||
@@ -191,6 +196,7 @@ describe('Hooks', () => {
|
||||
ready: true,
|
||||
'config:resolved': true,
|
||||
'prepare:types': true,
|
||||
'prepare:tsconfig': true,
|
||||
'prepare:publicPaths': true,
|
||||
'build:before': true,
|
||||
'build:done': true,
|
||||
|
||||
@@ -84,6 +84,51 @@ describe('Output Directory Structure', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should route content script CSS with Rollup strict deprecations enabled', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile(
|
||||
'entrypoints/content.content/index.ts',
|
||||
`import './style.css';
|
||||
export default defineContentScript({
|
||||
matches: ["*://*/*"],
|
||||
main: () => {},
|
||||
})`,
|
||||
);
|
||||
project.addFile(
|
||||
'entrypoints/content.content/style.css',
|
||||
`body { color: purple }`,
|
||||
);
|
||||
|
||||
await project.build({
|
||||
vite: () => ({
|
||||
build: {
|
||||
rollupOptions: {
|
||||
strictDeprecations: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
expect(
|
||||
await project.serializeOutput([
|
||||
'.output/chrome-mv3/content-scripts/content.js',
|
||||
]),
|
||||
).toMatchInlineSnapshot(`
|
||||
".output/chrome-mv3/content-scripts/content.css
|
||||
----------------------------------------
|
||||
body{color:purple}
|
||||
|
||||
================================================================================
|
||||
.output/chrome-mv3/content-scripts/content.js
|
||||
----------------------------------------
|
||||
<contents-ignored>
|
||||
================================================================================
|
||||
.output/chrome-mv3/manifest.json
|
||||
----------------------------------------
|
||||
{"manifest_version":3,"name":"E2E Extension","description":"Example description","version":"0.0.0","content_scripts":[{"matches":["*://*/*"],"css":["content-scripts/content.css"],"js":["content-scripts/content.js"]}]}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should allow inputs with invalid JS variable names, like dashes', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { TestProject } from '../utils';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
describe('TypeScript Project', () => {
|
||||
it('should generate defined constants correctly', async () => {
|
||||
@@ -333,25 +334,63 @@ describe('TypeScript Project', () => {
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@": [".."],
|
||||
"@/*": ["../*"],
|
||||
"~": [".."],
|
||||
"~/*": ["../*"],
|
||||
"@@": [".."],
|
||||
"@@/*": ["../*"],
|
||||
"~~": [".."],
|
||||
"~~/*": ["../*"]
|
||||
"@": [
|
||||
".."
|
||||
],
|
||||
"@/*": [
|
||||
"../*"
|
||||
],
|
||||
"~": [
|
||||
".."
|
||||
],
|
||||
"~/*": [
|
||||
"../*"
|
||||
],
|
||||
"@@": [
|
||||
".."
|
||||
],
|
||||
"@@/*": [
|
||||
"../*"
|
||||
],
|
||||
"~~": [
|
||||
".."
|
||||
],
|
||||
"~~/*": [
|
||||
"../*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"../**/*",
|
||||
"./wxt.d.ts"
|
||||
],
|
||||
"exclude": ["../.output"]
|
||||
"exclude": [
|
||||
"../**/node_modules",
|
||||
"../.output"
|
||||
]
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should allow updating the project's tsconfig via hooks", async () => {
|
||||
const project = new TestProject();
|
||||
project.setConfigFileConfig({});
|
||||
project.addFile('entrypoints/unlisted.html', '<html></html>');
|
||||
|
||||
await project.prepare({
|
||||
hooks: {
|
||||
'prepare:tsconfig': (_, { tsconfig }) => {
|
||||
tsconfig.test = 'test';
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const actual = JSON.parse(
|
||||
await readFile(project.resolvePath('.wxt/tsconfig.json'), 'utf-8'),
|
||||
);
|
||||
expect(actual).toMatchObject({ test: 'test' });
|
||||
});
|
||||
|
||||
it('should generate correct path aliases for a custom srcDir', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile('src/entrypoints/unlisted.html', '<html></html>');
|
||||
@@ -377,21 +416,40 @@ describe('TypeScript Project', () => {
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@": ["../src"],
|
||||
"@/*": ["../src/*"],
|
||||
"~": ["../src"],
|
||||
"~/*": ["../src/*"],
|
||||
"@@": [".."],
|
||||
"@@/*": ["../*"],
|
||||
"~~": [".."],
|
||||
"~~/*": ["../*"]
|
||||
"@": [
|
||||
"../src"
|
||||
],
|
||||
"@/*": [
|
||||
"../src/*"
|
||||
],
|
||||
"~": [
|
||||
"../src"
|
||||
],
|
||||
"~/*": [
|
||||
"../src/*"
|
||||
],
|
||||
"@@": [
|
||||
".."
|
||||
],
|
||||
"@@/*": [
|
||||
"../*"
|
||||
],
|
||||
"~~": [
|
||||
".."
|
||||
],
|
||||
"~~/*": [
|
||||
"../*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"../**/*",
|
||||
"./wxt.d.ts"
|
||||
],
|
||||
"exclude": ["../.output"]
|
||||
"exclude": [
|
||||
"../**/node_modules",
|
||||
"../.output"
|
||||
]
|
||||
}"
|
||||
`);
|
||||
});
|
||||
@@ -425,23 +483,46 @@ describe('TypeScript Project', () => {
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"example": ["../example"],
|
||||
"example/*": ["../example/*"],
|
||||
"@": ["../src"],
|
||||
"@/*": ["../src/*"],
|
||||
"~": ["../src"],
|
||||
"~/*": ["../src/*"],
|
||||
"@@": [".."],
|
||||
"@@/*": ["../*"],
|
||||
"~~": [".."],
|
||||
"~~/*": ["../*"]
|
||||
"example": [
|
||||
"../example"
|
||||
],
|
||||
"example/*": [
|
||||
"../example/*"
|
||||
],
|
||||
"@": [
|
||||
"../src"
|
||||
],
|
||||
"@/*": [
|
||||
"../src/*"
|
||||
],
|
||||
"~": [
|
||||
"../src"
|
||||
],
|
||||
"~/*": [
|
||||
"../src/*"
|
||||
],
|
||||
"@@": [
|
||||
".."
|
||||
],
|
||||
"@@/*": [
|
||||
"../*"
|
||||
],
|
||||
"~~": [
|
||||
".."
|
||||
],
|
||||
"~~/*": [
|
||||
"../*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"../**/*",
|
||||
"./wxt.d.ts"
|
||||
],
|
||||
"exclude": ["../.output"]
|
||||
"exclude": [
|
||||
"../**/node_modules",
|
||||
"../.output"
|
||||
]
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { TestProject } from '../utils';
|
||||
|
||||
describe('User Config', () => {
|
||||
@@ -150,4 +150,41 @@ describe('User Config', () => {
|
||||
|
||||
await project.build({ configFile: 'foo.config.ts' });
|
||||
});
|
||||
|
||||
it('should pass watch options to the dev server', async () => {
|
||||
const project = new TestProject();
|
||||
const extendViteDevServerConfig = vi.fn();
|
||||
project.addFile('entrypoints/popup.html', '<html></html>');
|
||||
|
||||
const server = await project.startServer({
|
||||
watchOptions: {
|
||||
usePolling: true,
|
||||
interval: 1000,
|
||||
ignored: ['**/generated/**'],
|
||||
},
|
||||
hooks: {
|
||||
'vite:devServer:extendConfig': extendViteDevServerConfig,
|
||||
},
|
||||
webExt: {
|
||||
disabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const viteConfig = extendViteDevServerConfig.mock.calls[0]?.[0];
|
||||
expect(viteConfig?.server?.watch).toMatchObject({
|
||||
usePolling: true,
|
||||
interval: 1000,
|
||||
});
|
||||
expect(viteConfig?.server?.watch?.ignored).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining('.output/**'),
|
||||
expect.stringContaining('.wxt/**'),
|
||||
'**/generated/**',
|
||||
]),
|
||||
);
|
||||
} finally {
|
||||
await server.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.20.26",
|
||||
"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",
|
||||
@@ -48,12 +48,11 @@
|
||||
"nypm": "^0.6.5",
|
||||
"ohash": "^2.0.11",
|
||||
"open": "^11.0.0",
|
||||
"perfect-debounce": "^2.1.0",
|
||||
"picomatch": "^4.0.3",
|
||||
"prompts": "^2.4.2",
|
||||
"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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Hookable } from 'hookable';
|
||||
import { mkdir, readdir, rename, rmdir, stat } from 'node:fs/promises';
|
||||
import { dirname, extname, join, relative } from 'node:path';
|
||||
import { dirname, extname, join, relative, resolve } from 'node:path';
|
||||
import type * as vite from 'vite';
|
||||
import { ViteNodeRunner } from 'vite-node/client';
|
||||
import { ViteNodeServer } from 'vite-node/server';
|
||||
@@ -29,6 +29,11 @@ import {
|
||||
} from '../../utils/virtual-modules';
|
||||
import * as wxtPlugins from './plugins';
|
||||
|
||||
interface RollupAssetNameInfo {
|
||||
name?: string;
|
||||
names?: string[];
|
||||
}
|
||||
|
||||
export async function createViteBuilder(
|
||||
wxtConfig: ResolvedConfig,
|
||||
hooks: Hookable<WxtHooks>,
|
||||
@@ -67,7 +72,13 @@ export async function createViteBuilder(
|
||||
|
||||
config.server ??= {};
|
||||
config.server.watch = {
|
||||
ignored: [`${wxtConfig.outBaseDir}/**`, `${wxtConfig.wxtDir}/**`],
|
||||
...wxtConfig.watchOptions,
|
||||
ignored: [
|
||||
`${wxtConfig.outBaseDir}/**`,
|
||||
`${wxtConfig.wxtDir}/**`,
|
||||
...getRunnerProfileWatchIgnores(wxtConfig),
|
||||
...toArray(wxtConfig.watchOptions.ignored ?? []),
|
||||
],
|
||||
};
|
||||
|
||||
// TODO: Remove once https://github.com/wxt-dev/wxt/pull/1411 is merged
|
||||
@@ -166,10 +177,12 @@ export async function createViteBuilder(
|
||||
),
|
||||
// Output content script CSS to `content-scripts/`, but all other scripts are written to
|
||||
// `assets/`.
|
||||
assetFileNames: ({ name }) => {
|
||||
assetFileNames: (assetInfo) => {
|
||||
if (
|
||||
entrypoint.type === 'content-script' &&
|
||||
name?.endsWith('css')
|
||||
getRollupAssetNames(assetInfo).some((name) =>
|
||||
name.endsWith('css'),
|
||||
)
|
||||
) {
|
||||
return `content-scripts/${entrypoint.name}.[ext]`;
|
||||
} else {
|
||||
@@ -392,6 +405,64 @@ export async function createViteBuilder(
|
||||
};
|
||||
}
|
||||
|
||||
export function getRunnerProfileWatchIgnores(
|
||||
wxtConfig: ResolvedConfig,
|
||||
): string[] {
|
||||
const root = normalizePath(wxtConfig.root);
|
||||
const chromiumArgProfiles = extractPathArgs(
|
||||
wxtConfig.runnerConfig.config?.chromiumArgs,
|
||||
'--user-data-dir',
|
||||
);
|
||||
const firefoxArgProfiles = extractPathArgs(
|
||||
wxtConfig.runnerConfig.config?.firefoxArgs,
|
||||
'-profile',
|
||||
);
|
||||
const profiles = [
|
||||
wxtConfig.runnerConfig.config?.chromiumProfile,
|
||||
wxtConfig.runnerConfig.config?.firefoxProfile,
|
||||
...chromiumArgProfiles,
|
||||
...firefoxArgProfiles,
|
||||
].filter((profile): profile is string => typeof profile === 'string');
|
||||
|
||||
return Array.from(
|
||||
new Set(
|
||||
profiles
|
||||
.map((profile) => normalizePath(resolve(wxtConfig.root, profile)))
|
||||
// Avoid accidentally disabling all file watching.
|
||||
.filter((profilePath) => profilePath !== root)
|
||||
.map((profilePath) => `${profilePath}/**`),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function extractPathArgs(args: string[] | undefined, flag: string): string[] {
|
||||
if (!args?.length) return [];
|
||||
|
||||
const paths: string[] = [];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
|
||||
if (arg.startsWith(`${flag}=`)) {
|
||||
const value = arg.slice(flag.length + 1).trim();
|
||||
if (value) paths.push(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === flag) {
|
||||
const nextValue = args[i + 1]?.trim();
|
||||
if (nextValue) paths.push(nextValue);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
function getRollupAssetNames(assetInfo: RollupAssetNameInfo): string[] {
|
||||
if (Array.isArray(assetInfo.names)) return assetInfo.names;
|
||||
return assetInfo.name ? [assetInfo.name] : [];
|
||||
}
|
||||
|
||||
function getBuildOutputChunks(
|
||||
result: Awaited<ReturnType<typeof vite.build>>,
|
||||
): BuildStepOutput['chunks'] {
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function clean(config?: string | InlineConfig) {
|
||||
const tempDirs = [
|
||||
'node_modules/.vite',
|
||||
'node_modules/.cache',
|
||||
'**/.wxt',
|
||||
'{,**/}.wxt',
|
||||
`${path.relative(root, wxt.config.outBaseDir)}/*`,
|
||||
];
|
||||
wxt.logger.debug(
|
||||
|
||||
@@ -93,14 +93,6 @@ async function createServerInternal(): Promise<WxtDevServer> {
|
||||
const reloadOnChange = createFileReloader(server);
|
||||
server.watcher.on('all', async (...args) => {
|
||||
await reloadOnChange(args[0], args[1]);
|
||||
|
||||
// Restart keyboard shortcuts after file is changed - for some reason they stop working.
|
||||
keyboardShortcuts.start();
|
||||
});
|
||||
|
||||
keyboardShortcuts.printHelp({
|
||||
canReopenBrowser:
|
||||
!wxt.config.runnerConfig.config.disabled && !!runner.canOpen?.(),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -137,6 +129,10 @@ async function createServerInternal(): Promise<WxtDevServer> {
|
||||
runner = await createExtensionRunner();
|
||||
await runner.openBrowser();
|
||||
keyboardShortcuts.start();
|
||||
keyboardShortcuts.printHelp({
|
||||
canReopenBrowser:
|
||||
!wxt.config.runnerConfig.config.disabled && !!runner.canOpen?.(),
|
||||
});
|
||||
},
|
||||
};
|
||||
const keyboardShortcuts = createKeyboardShortcuts(server);
|
||||
@@ -152,7 +148,10 @@ async function createServerInternal(): Promise<WxtDevServer> {
|
||||
logBabelSyntaxError(err);
|
||||
wxt.logger.info('Waiting for syntax error to be fixed...');
|
||||
await new Promise<void>((resolve) => {
|
||||
const watcher = chokidar.watch(err.id, { ignoreInitial: true });
|
||||
const watcher = chokidar.watch(err.id, {
|
||||
...wxt.config.watchOptions,
|
||||
ignoreInitial: true,
|
||||
});
|
||||
watcher.on('all', () => {
|
||||
watcher.close();
|
||||
wxt.logger.info('Syntax error resolved, rebuilding...');
|
||||
@@ -172,6 +171,11 @@ async function createServerInternal(): Promise<WxtDevServer> {
|
||||
|
||||
// Open browser after everything is ready to go.
|
||||
await runner.openBrowser();
|
||||
keyboardShortcuts.start();
|
||||
keyboardShortcuts.printHelp({
|
||||
canReopenBrowser:
|
||||
!wxt.config.runnerConfig.config.disabled && !!runner.canOpen?.(),
|
||||
});
|
||||
};
|
||||
|
||||
builderServer.on?.('close', () => keyboardShortcuts.stop());
|
||||
|
||||
@@ -283,41 +283,42 @@ async function getTsConfigEntry(): Promise<WxtDirFileEntry> {
|
||||
if (res.startsWith('.') || res.startsWith('/')) return res;
|
||||
return './' + res;
|
||||
};
|
||||
const paths = Object.entries(wxt.config.alias)
|
||||
.flatMap(([alias, absolutePath]) => {
|
||||
const aliasPath = getTsconfigPath(absolutePath);
|
||||
return [
|
||||
`"${alias}": ["${aliasPath}"]`,
|
||||
`"${alias}/*": ["${aliasPath}/*"]`,
|
||||
];
|
||||
})
|
||||
.map((line) => ` ${line}`)
|
||||
.join(',\n');
|
||||
|
||||
const text = `{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
${paths}
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"${getTsconfigPath(wxt.config.root)}/**/*",
|
||||
"./wxt.d.ts"
|
||||
],
|
||||
"exclude": ["${getTsconfigPath(wxt.config.outBaseDir)}"]
|
||||
}`;
|
||||
const paths = Object.fromEntries(
|
||||
Object.entries(wxt.config.alias)
|
||||
.map(([alias, absolutePath]) => [alias, getTsconfigPath(absolutePath)])
|
||||
.flatMap(([alias, path]) => [
|
||||
// Add alias for files
|
||||
[alias, [path]],
|
||||
// Add alias for all files in a directory
|
||||
[alias + '/*', [path + '/*']],
|
||||
]),
|
||||
);
|
||||
|
||||
const tsconfig = {
|
||||
compilerOptions: {
|
||||
target: 'ESNext',
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'Bundler',
|
||||
noEmit: true,
|
||||
esModuleInterop: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
resolveJsonModule: true,
|
||||
strict: true,
|
||||
skipLibCheck: true,
|
||||
paths,
|
||||
},
|
||||
include: [`${getTsconfigPath(wxt.config.root)}/**/*`, './wxt.d.ts'],
|
||||
exclude: [
|
||||
getTsconfigPath(wxt.config.root) + '/**/node_modules',
|
||||
getTsconfigPath(wxt.config.outBaseDir),
|
||||
],
|
||||
};
|
||||
|
||||
await wxt.hooks.callHook('prepare:tsconfig', wxt, { tsconfig });
|
||||
|
||||
return {
|
||||
path: 'tsconfig.json',
|
||||
text,
|
||||
text: JSON.stringify(tsconfig, null, 2),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ export async function initialize(options: {
|
||||
type: () => (options.packageManager == null ? 'select' : undefined),
|
||||
message: 'Package Manager',
|
||||
choices: [
|
||||
{ title: styleText('magenta', 'bun'), value: 'bun' },
|
||||
{ title: styleText('red', 'npm'), value: 'npm' },
|
||||
{ title: styleText('yellow', 'pnpm'), value: 'pnpm' },
|
||||
{ title: styleText('cyan', 'yarn'), value: 'yarn' },
|
||||
{ title: styleText('magenta', 'bun'), value: 'bun' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -54,7 +54,7 @@ export async function initialize(options: {
|
||||
onCancel: () => process.exit(1),
|
||||
},
|
||||
);
|
||||
input.directory ??= options.directory;
|
||||
input.directory ||= options.directory || '.';
|
||||
input.template ??= defaultTemplate;
|
||||
input.packageManager ??= options.packageManager;
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ import spawn from 'nano-spawn';
|
||||
import path from 'node:path';
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
import { pnpm } from '../pnpm';
|
||||
import { describeWithBin } from '../../utils/testing/fixtures';
|
||||
|
||||
process.env.WXT_PNPM_IGNORE_WORKSPACE = 'true';
|
||||
|
||||
describe('PNPM Package Management Utils', () => {
|
||||
describeWithBin('pnpm', 'PNPM Package Management Utils', () => {
|
||||
describe('listDependencies', () => {
|
||||
const cwd = path.resolve(__dirname, 'fixtures/simple-pnpm-project');
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import path from 'node:path';
|
||||
import { yarn } from '../yarn';
|
||||
import { describeWithBin } from '../../utils/testing/fixtures';
|
||||
|
||||
describe('Yarn Package Management Utils', () => {
|
||||
describeWithBin('yarn', 'Yarn Package Management Utils', () => {
|
||||
describe('listDependencies', () => {
|
||||
const cwd = path.resolve(__dirname, 'fixtures/simple-yarn-project');
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ export async function resolveConfig(
|
||||
alias,
|
||||
experimental: defu(mergedConfig.experimental, {}),
|
||||
suppressWarnings: mergedConfig.suppressWarnings ?? {},
|
||||
watchOptions: mergedConfig.watchOptions ?? {},
|
||||
dev: {
|
||||
server: devServerConfig,
|
||||
reloadCommand,
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { createFileReloader } from '../create-file-reloader';
|
||||
import { findEntrypoints, rebuild } from '../building';
|
||||
import {
|
||||
fakeBackgroundEntrypoint,
|
||||
fakeBuildOutput,
|
||||
fakeDevServer,
|
||||
fakeOutputChunk,
|
||||
fakePopupEntrypoint,
|
||||
setFakeWxt,
|
||||
} from '../testing/fake-objects';
|
||||
|
||||
vi.mock('../building', async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import('../building')>('../building');
|
||||
return {
|
||||
...actual,
|
||||
findEntrypoints: vi.fn(),
|
||||
rebuild: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('createFileReloader', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
setFakeWxt({
|
||||
config: {
|
||||
root: '/root',
|
||||
entrypointsDir: '/root/src/entrypoints',
|
||||
dev: {
|
||||
server: {
|
||||
watchDebounce: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
vi.mocked(findEntrypoints).mockResolvedValue([]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should detect relevant file changes even when noisy file events happen first', async () => {
|
||||
const relevantFile = '/root/src/entrypoints/background.ts';
|
||||
const noisyProfileFile =
|
||||
'/root/private/.dev-profile/Default/Cache/Cache_Data/d573fa6484e43cf9_0';
|
||||
const backgroundEntrypoint = fakeBackgroundEntrypoint({
|
||||
inputPath: relevantFile,
|
||||
skipped: false,
|
||||
});
|
||||
const currentOutput = fakeBuildOutput({
|
||||
steps: [
|
||||
{
|
||||
entrypoints: backgroundEntrypoint,
|
||||
chunks: [fakeOutputChunk({ moduleIds: [relevantFile] })],
|
||||
},
|
||||
],
|
||||
publicAssets: [],
|
||||
});
|
||||
const server = fakeDevServer({
|
||||
currentOutput,
|
||||
reloadExtension: vi.fn(),
|
||||
});
|
||||
|
||||
vi.mocked(rebuild).mockResolvedValue({
|
||||
output: currentOutput,
|
||||
manifest: currentOutput.manifest,
|
||||
warnings: [],
|
||||
});
|
||||
vi.mocked(findEntrypoints).mockResolvedValue([backgroundEntrypoint]);
|
||||
|
||||
const reloadOnChange = createFileReloader(server);
|
||||
|
||||
const fixedFirst = reloadOnChange('change', noisyProfileFile);
|
||||
await vi.advanceTimersByTimeAsync(50);
|
||||
const fixedSecond = reloadOnChange('change', relevantFile);
|
||||
await vi.advanceTimersByTimeAsync(500);
|
||||
await Promise.all([fixedFirst, fixedSecond]);
|
||||
|
||||
expect(rebuild).toBeCalledTimes(1);
|
||||
const [allEntrypoints, rebuiltGroups] = vi.mocked(rebuild).mock.calls[0];
|
||||
expect(
|
||||
allEntrypoints.some((entry) => entry.inputPath === relevantFile),
|
||||
).toBe(true);
|
||||
expect(
|
||||
rebuiltGroups.flat().some((entry) => entry.inputPath === relevantFile),
|
||||
).toBe(true);
|
||||
expect(server.reloadExtension).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should rebuild and reload extension when a new entrypoint is added', async () => {
|
||||
const backgroundFile = '/root/src/entrypoints/background.ts';
|
||||
const newEntrypointFile = '/root/src/entrypoints/popup.html';
|
||||
const backgroundEntrypoint = fakeBackgroundEntrypoint({
|
||||
inputPath: backgroundFile,
|
||||
skipped: false,
|
||||
});
|
||||
const popupEntrypoint = fakePopupEntrypoint({
|
||||
inputPath: newEntrypointFile,
|
||||
skipped: false,
|
||||
});
|
||||
const currentOutput = fakeBuildOutput({
|
||||
steps: [
|
||||
{
|
||||
entrypoints: backgroundEntrypoint,
|
||||
chunks: [fakeOutputChunk({ moduleIds: [backgroundFile] })],
|
||||
},
|
||||
],
|
||||
publicAssets: [],
|
||||
});
|
||||
const server = fakeDevServer({
|
||||
currentOutput,
|
||||
reloadExtension: vi.fn(),
|
||||
});
|
||||
|
||||
vi.mocked(findEntrypoints).mockResolvedValue([
|
||||
backgroundEntrypoint,
|
||||
popupEntrypoint,
|
||||
]);
|
||||
vi.mocked(rebuild).mockResolvedValue({
|
||||
output: currentOutput,
|
||||
manifest: currentOutput.manifest,
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
const reloadOnChange = createFileReloader(server);
|
||||
const trigger = reloadOnChange('add', newEntrypointFile);
|
||||
await vi.advanceTimersByTimeAsync(500);
|
||||
await trigger;
|
||||
|
||||
expect(rebuild).toBeCalledTimes(1);
|
||||
const [allEntrypoints, rebuiltGroups, cachedOutput] =
|
||||
vi.mocked(rebuild).mock.calls[0];
|
||||
expect(allEntrypoints).toEqual([backgroundEntrypoint, popupEntrypoint]);
|
||||
expect(
|
||||
rebuiltGroups
|
||||
.flat()
|
||||
.some((entry) => entry.inputPath === newEntrypointFile),
|
||||
).toBe(true);
|
||||
expect(cachedOutput).toEqual(currentOutput);
|
||||
expect(server.reloadExtension).toBeCalledTimes(1);
|
||||
expect(server.reloadContentScript).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should ignore entrypoint directory changes when no new entrypoints are found', async () => {
|
||||
const backgroundFile = '/root/src/entrypoints/background.ts';
|
||||
const ignoredEntrypointDirFile = '/root/src/entrypoints/.DS_Store';
|
||||
const backgroundEntrypoint = fakeBackgroundEntrypoint({
|
||||
inputPath: backgroundFile,
|
||||
skipped: false,
|
||||
});
|
||||
const currentOutput = fakeBuildOutput({
|
||||
steps: [
|
||||
{
|
||||
entrypoints: backgroundEntrypoint,
|
||||
chunks: [fakeOutputChunk({ moduleIds: [backgroundFile] })],
|
||||
},
|
||||
],
|
||||
publicAssets: [],
|
||||
});
|
||||
const server = fakeDevServer({
|
||||
currentOutput,
|
||||
reloadContentScript: vi.fn(),
|
||||
reloadExtension: vi.fn(),
|
||||
});
|
||||
|
||||
vi.mocked(findEntrypoints).mockResolvedValue([backgroundEntrypoint]);
|
||||
|
||||
const reloadOnChange = createFileReloader(server);
|
||||
const trigger = reloadOnChange('add', ignoredEntrypointDirFile);
|
||||
await vi.advanceTimersByTimeAsync(500);
|
||||
await trigger;
|
||||
|
||||
expect(rebuild).not.toBeCalled();
|
||||
expect(server.reloadContentScript).not.toBeCalled();
|
||||
expect(server.reloadExtension).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -1693,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'),
|
||||
);
|
||||
@@ -2125,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',
|
||||
|
||||
@@ -55,6 +55,34 @@ describe('Transform Utils', () => {
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should preserve spread config when removing the main field', () => {
|
||||
const input = `
|
||||
const sharedConfig = {
|
||||
matches: ['*://*/*'],
|
||||
};
|
||||
|
||||
export default defineContentScript({
|
||||
...sharedConfig,
|
||||
runAt: 'document_idle',
|
||||
main(ctx) {
|
||||
console.log(ctx);
|
||||
},
|
||||
})
|
||||
`;
|
||||
const expected = `const sharedConfig = {
|
||||
matches: ['*://*/*'],
|
||||
};
|
||||
|
||||
export default defineContentScript({
|
||||
...sharedConfig,
|
||||
runAt: 'document_idle'
|
||||
})`;
|
||||
|
||||
const actual = removeMainFunctionCode(input).code;
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should remove unused imports', () => {
|
||||
const input = `
|
||||
import { defineBackground } from "#imports"
|
||||
|
||||
@@ -252,6 +252,60 @@ describe('Detect Dev Changes', () => {
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should ignore unrelated changed files when checking html-only reloads', async () => {
|
||||
const changedPath = '/root/page1.html';
|
||||
const unrelatedPath =
|
||||
'/root/private/.dev-profile/Default/Cache/Cache_Data/1004_0';
|
||||
const htmlPage1 = fakePopupEntrypoint({
|
||||
inputPath: changedPath,
|
||||
});
|
||||
const htmlPage2 = fakeOptionsEntrypoint({
|
||||
inputPath: '/root/page2.html',
|
||||
});
|
||||
const htmlPage3 = fakeGenericEntrypoint({
|
||||
type: 'sandbox',
|
||||
inputPath: '/root/page3.html',
|
||||
});
|
||||
|
||||
const step1: BuildStepOutput = {
|
||||
entrypoints: [htmlPage1, htmlPage2],
|
||||
chunks: [
|
||||
fakeOutputAsset({
|
||||
fileName: 'page1.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
const step2: BuildStepOutput = {
|
||||
entrypoints: [htmlPage3],
|
||||
chunks: [
|
||||
fakeOutputAsset({
|
||||
fileName: 'page2.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
const currentOutput: BuildOutput = {
|
||||
manifest: fakeManifest(),
|
||||
publicAssets: [],
|
||||
steps: [step1, step2],
|
||||
};
|
||||
const expected: DevModeChange = {
|
||||
type: 'html-reload',
|
||||
cachedOutput: {
|
||||
...currentOutput,
|
||||
steps: [step2],
|
||||
},
|
||||
rebuildGroups: [[htmlPage1, htmlPage2]],
|
||||
};
|
||||
|
||||
const actual = detectDevChanges(
|
||||
[unrelatedPath, changedPath],
|
||||
currentOutput,
|
||||
);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should detect changes to entrypoints/<name>/index.html files', async () => {
|
||||
const changedPath = '/root/page1/index.html';
|
||||
const htmlPage1 = fakePopupEntrypoint({
|
||||
|
||||
@@ -45,30 +45,35 @@ export function detectDevChanges(
|
||||
changedFiles: string[],
|
||||
currentOutput: BuildOutput,
|
||||
): DevModeChange {
|
||||
const isConfigChange = some(
|
||||
const relevantChangedFiles = getRelevantDevChangedFiles(
|
||||
changedFiles,
|
||||
currentOutput,
|
||||
);
|
||||
|
||||
const isConfigChange = some(
|
||||
relevantChangedFiles,
|
||||
(file) => file === wxt.config.userConfigMetadata.configFile,
|
||||
);
|
||||
if (isConfigChange) return { type: 'full-restart' };
|
||||
|
||||
const isWxtModuleChange = some(changedFiles, (file) =>
|
||||
const isWxtModuleChange = some(relevantChangedFiles, (file) =>
|
||||
file.startsWith(wxt.config.modulesDir),
|
||||
);
|
||||
if (isWxtModuleChange) return { type: 'full-restart' };
|
||||
|
||||
const isRunnerChange = some(
|
||||
changedFiles,
|
||||
relevantChangedFiles,
|
||||
(file) => file === wxt.config.runnerConfig.configFile,
|
||||
);
|
||||
if (isRunnerChange) return { type: 'browser-restart' };
|
||||
|
||||
const changedSteps = new Set(
|
||||
changedFiles.flatMap((changedFile) =>
|
||||
relevantChangedFiles.flatMap((changedFile) =>
|
||||
findEffectedSteps(changedFile, currentOutput),
|
||||
),
|
||||
);
|
||||
if (changedSteps.size === 0) {
|
||||
const hasPublicChange = some(changedFiles, (file) =>
|
||||
const hasPublicChange = some(relevantChangedFiles, (file) =>
|
||||
file.startsWith(wxt.config.publicDir),
|
||||
);
|
||||
if (hasPublicChange) {
|
||||
@@ -102,8 +107,8 @@ export function detectDevChanges(
|
||||
}
|
||||
|
||||
const isOnlyHtmlChanges =
|
||||
changedFiles.length > 0 &&
|
||||
every(changedFiles, (file) => file.endsWith('.html'));
|
||||
relevantChangedFiles.length > 0 &&
|
||||
every(relevantChangedFiles, (file) => file.endsWith('.html'));
|
||||
if (isOnlyHtmlChanges) {
|
||||
return {
|
||||
type: 'html-reload',
|
||||
@@ -134,6 +139,19 @@ export function detectDevChanges(
|
||||
};
|
||||
}
|
||||
|
||||
export function getRelevantDevChangedFiles(
|
||||
changedFiles: string[],
|
||||
currentOutput: BuildOutput,
|
||||
): string[] {
|
||||
return Array.from(new Set(changedFiles)).filter((changedFile) => {
|
||||
if (changedFile === wxt.config.userConfigMetadata.configFile) return true;
|
||||
if (changedFile.startsWith(wxt.config.modulesDir)) return true;
|
||||
if (changedFile === wxt.config.runnerConfig.configFile) return true;
|
||||
if (changedFile.startsWith(wxt.config.publicDir)) return true;
|
||||
return findEffectedSteps(changedFile, currentOutput).length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* For a single change, return all the step of the build output that were
|
||||
* effected by it.
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import { debounce } from 'perfect-debounce';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import { relative } from 'node:path';
|
||||
import { BuildStepOutput, EntrypointGroup, WxtDevServer } from '../../types';
|
||||
import {
|
||||
BuildOutput,
|
||||
BuildStepOutput,
|
||||
EntrypointGroup,
|
||||
WxtDevServer,
|
||||
} from '../../types';
|
||||
import { wxt } from '../wxt';
|
||||
import { detectDevChanges, findEntrypoints, rebuild } from './building';
|
||||
import {
|
||||
detectDevChanges,
|
||||
findEntrypoints,
|
||||
getRelevantDevChangedFiles,
|
||||
groupEntrypoints,
|
||||
rebuild,
|
||||
} from './building';
|
||||
import { getEntrypointBundlePath, isHtmlEntrypoint } from './entrypoints';
|
||||
import { getContentScriptCssFiles, getContentScriptsCssMap } from './manifest';
|
||||
import {
|
||||
@@ -12,6 +22,8 @@ import {
|
||||
} from './content-scripts';
|
||||
import { isBabelSyntaxError, logBabelSyntaxError } from './syntax-errors';
|
||||
import { styleText } from 'node:util';
|
||||
import { filterTruthy, toArray } from './arrays';
|
||||
import { normalizePath } from './paths';
|
||||
|
||||
/**
|
||||
* Returns a function responsible for reloading different parts of the extension
|
||||
@@ -20,22 +32,44 @@ import { styleText } from 'node:util';
|
||||
export function createFileReloader(server: WxtDevServer) {
|
||||
const fileChangedMutex = new Mutex();
|
||||
const changeQueue: Array<[string, string]> = [];
|
||||
let processLoop: Promise<void> | undefined;
|
||||
|
||||
const cb = async (event: string, path: string) => {
|
||||
changeQueue.push([event, path]);
|
||||
|
||||
const processQueue = async () => {
|
||||
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;
|
||||
if (server.currentOutput == null) return;
|
||||
|
||||
const normalizedFileChanges = fileChanges.map(normalizePath);
|
||||
const relevantFileChanges = getRelevantDevChangedFiles(
|
||||
fileChanges,
|
||||
server.currentOutput,
|
||||
);
|
||||
const normalizedEntrypointsDir = normalizePath(wxt.config.entrypointsDir);
|
||||
const hasEntrypointDirChange = normalizedFileChanges.some(
|
||||
(file) =>
|
||||
file === normalizedEntrypointsDir ||
|
||||
file.startsWith(`${normalizedEntrypointsDir}/`),
|
||||
);
|
||||
if (relevantFileChanges.length === 0 && !hasEntrypointDirChange) return;
|
||||
|
||||
await wxt.reloadConfig();
|
||||
const allEntrypoints = await findEntrypoints();
|
||||
const allEntrypointGroups = groupEntrypoints(allEntrypoints);
|
||||
const newEntrypointGroups = getNewEntrypointGroups(
|
||||
normalizedFileChanges,
|
||||
allEntrypointGroups,
|
||||
server.currentOutput,
|
||||
);
|
||||
if (relevantFileChanges.length === 0 && newEntrypointGroups.length === 0)
|
||||
return;
|
||||
|
||||
const changes = detectDevChanges(fileChanges, server.currentOutput);
|
||||
if (changes.type === 'no-change') return;
|
||||
const changes = detectDevChanges(
|
||||
relevantFileChanges,
|
||||
server.currentOutput,
|
||||
);
|
||||
|
||||
if (changes.type === 'full-restart') {
|
||||
wxt.logger.info('Config changed, restarting server...');
|
||||
@@ -48,46 +82,58 @@ export function createFileReloader(server: WxtDevServer) {
|
||||
server.restartBrowser();
|
||||
return;
|
||||
}
|
||||
const hasNewEntrypoints = newEntrypointGroups.length > 0;
|
||||
if (changes.type === 'no-change' && !hasNewEntrypoints) return;
|
||||
|
||||
const changedFilesToLog = Array.from(
|
||||
new Set([
|
||||
...relevantFileChanges,
|
||||
...newEntrypointGroups
|
||||
.flatMap((group) => toArray(group))
|
||||
.map((entry) => entry.inputPath),
|
||||
]),
|
||||
);
|
||||
|
||||
// Log the entrypoints that were effected
|
||||
wxt.logger.info(
|
||||
`Changed: ${Array.from(new Set(fileChanges))
|
||||
`Changed: ${changedFilesToLog
|
||||
.map((file) => styleText('dim', relative(wxt.config.root, file)))
|
||||
.join(', ')}`,
|
||||
);
|
||||
|
||||
// Rebuild entrypoints on change
|
||||
const allEntrypoints = await findEntrypoints();
|
||||
const rebuildGroups =
|
||||
changes.type === 'no-change'
|
||||
? newEntrypointGroups
|
||||
: mergeEntrypointGroups(
|
||||
getLatestRebuildGroups(
|
||||
changes.rebuildGroups,
|
||||
allEntrypointGroups,
|
||||
),
|
||||
newEntrypointGroups,
|
||||
);
|
||||
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,
|
||||
rebuildGroups,
|
||||
changes.type === 'no-change'
|
||||
? server.currentOutput
|
||||
: 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);
|
||||
if (hasNewEntrypoints || changes.type === 'extension-reload') {
|
||||
server.reloadExtension();
|
||||
wxt.logger.success(`Reloaded extension`);
|
||||
} else if (changes.type === 'html-reload') {
|
||||
const { reloadedNames } = reloadHtmlPages(rebuildGroups, server);
|
||||
wxt.logger.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
|
||||
} else if (changes.type === 'content-script-reload') {
|
||||
reloadContentScripts(changes.changedSteps, server);
|
||||
|
||||
const rebuiltNames = changes.rebuildGroups
|
||||
.flat()
|
||||
.map((entry) => entry.name);
|
||||
wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
|
||||
break;
|
||||
const rebuiltNames = rebuildGroups.flat().map((entry) => entry.name);
|
||||
wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
|
||||
}
|
||||
} catch {
|
||||
// Catch build errors instead of crashing. Don't log error either, builder should have already logged it
|
||||
@@ -103,12 +149,98 @@ export function createFileReloader(server: WxtDevServer) {
|
||||
});
|
||||
};
|
||||
|
||||
return debounce(cb, wxt.config.dev.server!.watchDebounce, {
|
||||
leading: true,
|
||||
trailing: false,
|
||||
const waitForDebounceWindow = async () => {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, wxt.config.dev.server!.watchDebounce);
|
||||
});
|
||||
};
|
||||
|
||||
const queueWorker = async () => {
|
||||
while (true) {
|
||||
await processQueue();
|
||||
|
||||
await waitForDebounceWindow();
|
||||
if (changeQueue.length === 0) break;
|
||||
}
|
||||
};
|
||||
|
||||
return async (event: string, path: string) => {
|
||||
// Queue every event before debouncing so we never drop changes.
|
||||
changeQueue.push([event, path]);
|
||||
|
||||
processLoop ??= queueWorker().finally(() => {
|
||||
processLoop = undefined;
|
||||
});
|
||||
await processLoop;
|
||||
};
|
||||
}
|
||||
|
||||
function getNewEntrypointGroups(
|
||||
normalizedFileChanges: string[],
|
||||
allEntrypointGroups: EntrypointGroup[],
|
||||
currentOutput: BuildOutput,
|
||||
): EntrypointGroup[] {
|
||||
const changedFiles = new Set(normalizedFileChanges);
|
||||
const builtEntrypointPaths = new Set(
|
||||
currentOutput.steps.flatMap((step) =>
|
||||
toArray(step.entrypoints).map((entry) => normalizePath(entry.inputPath)),
|
||||
),
|
||||
);
|
||||
|
||||
return allEntrypointGroups.filter((group) => {
|
||||
const groupEntrypoints = toArray(group);
|
||||
const hasNewEntrypoint = groupEntrypoints.some(
|
||||
(entry) => !builtEntrypointPaths.has(normalizePath(entry.inputPath)),
|
||||
);
|
||||
const changedEntrypoint = groupEntrypoints.some((entry) =>
|
||||
changedFiles.has(normalizePath(entry.inputPath)),
|
||||
);
|
||||
return hasNewEntrypoint && changedEntrypoint;
|
||||
});
|
||||
}
|
||||
|
||||
function getLatestRebuildGroups(
|
||||
rebuildGroups: EntrypointGroup[],
|
||||
allEntrypointGroups: EntrypointGroup[],
|
||||
): EntrypointGroup[] {
|
||||
const groupByEntrypointPath = new Map<string, EntrypointGroup>();
|
||||
|
||||
allEntrypointGroups.forEach((group) => {
|
||||
toArray(group).forEach((entry) => {
|
||||
groupByEntrypointPath.set(normalizePath(entry.inputPath), group);
|
||||
});
|
||||
});
|
||||
|
||||
return mergeEntrypointGroups(
|
||||
rebuildGroups.flatMap((group) => {
|
||||
return filterTruthy(
|
||||
toArray(group).map((entry) =>
|
||||
groupByEntrypointPath.get(normalizePath(entry.inputPath)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function mergeEntrypointGroups(
|
||||
...groups: EntrypointGroup[][]
|
||||
): EntrypointGroup[] {
|
||||
const deduped = new Map<string, EntrypointGroup>();
|
||||
|
||||
groups.flat().forEach((group) => {
|
||||
deduped.set(getEntrypointGroupKey(group), group);
|
||||
});
|
||||
|
||||
return [...deduped.values()];
|
||||
}
|
||||
|
||||
function getEntrypointGroupKey(group: EntrypointGroup): string {
|
||||
return toArray(group)
|
||||
.map((entry) => normalizePath(entry.inputPath))
|
||||
.sort()
|
||||
.join('|');
|
||||
}
|
||||
|
||||
/**
|
||||
* From the server, tell the client to reload content scripts from the provided
|
||||
* build step outputs.
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export { normalizePath } from './paths';
|
||||
export { getEntrypointBundlePath } from './entrypoints';
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -296,6 +296,7 @@ export const fakeResolvedConfig = fakeObjectCreator<ResolvedConfig>(() => {
|
||||
userConfigMetadata: {},
|
||||
alias: {},
|
||||
experimental: {},
|
||||
watchOptions: {},
|
||||
dev: {
|
||||
reloadCommand: 'Alt+R',
|
||||
},
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { describe } from 'vitest';
|
||||
|
||||
export function describeWithBin(
|
||||
bin: string,
|
||||
title: string,
|
||||
callback: () => void,
|
||||
) {
|
||||
if (process.env.CI === 'true') return describe(title, callback);
|
||||
|
||||
const result = spawnSync(bin, ['--version'], {
|
||||
stdio: 'ignore',
|
||||
shell: true,
|
||||
});
|
||||
if (result.status !== 0) return describe.skip(title, callback);
|
||||
return describe(title, callback);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ function emptyMainFunction(mod: ProxifiedModule): void {
|
||||
// ex: "fn({ ..., main: () => {} })" to "fn({ ... })"
|
||||
mod.exports.default.$ast.arguments[0].properties =
|
||||
mod.exports.default.$ast.arguments[0].properties.filter(
|
||||
(prop: any) => prop.key.name !== 'main',
|
||||
(prop: any) => prop.key?.name !== 'main',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,20 @@ export interface InlineConfig {
|
||||
* line `--mv2` or `--mv3` option.
|
||||
*/
|
||||
manifestVersion?: TargetManifestVersion;
|
||||
/**
|
||||
* Chokidar options used by dev-mode file watchers. This is useful in
|
||||
* containers, WSL, and network file systems where native file events can be
|
||||
* unreliable.
|
||||
*
|
||||
* @example
|
||||
* export default defineConfig({
|
||||
* watchOptions: {
|
||||
* usePolling: true,
|
||||
* interval: 1000,
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
watchOptions?: vite.WatchOptions;
|
||||
/**
|
||||
* Override the logger used.
|
||||
*
|
||||
@@ -159,6 +173,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
|
||||
@@ -1080,7 +1099,7 @@ export type UserManifest = {
|
||||
};
|
||||
};
|
||||
permissions?: (
|
||||
| Browser.runtime.ManifestPermissions
|
||||
| Browser.runtime.ManifestPermission
|
||||
| (string & Record<never, never>)
|
||||
)[];
|
||||
web_accessible_resources?:
|
||||
@@ -1254,6 +1273,11 @@ export interface ServerInfo {
|
||||
origin: string;
|
||||
}
|
||||
|
||||
export type PrepareTsconfigs = {
|
||||
/** The JSON contents of the `.wxt/tsconfig.json` file. */
|
||||
tsconfig: any;
|
||||
};
|
||||
|
||||
export type HookResult = Promise<void> | void;
|
||||
|
||||
export interface WxtHooks {
|
||||
@@ -1293,6 +1317,17 @@ export interface WxtHooks {
|
||||
* });
|
||||
*/
|
||||
'prepare:types': (wxt: Wxt, entries: WxtDirEntry[]) => HookResult;
|
||||
/**
|
||||
* Called before WXT writes your tsconfig to the disk, allowing full
|
||||
* customization by modifying the object by reference.
|
||||
*
|
||||
* @since 0.20.28
|
||||
* @example
|
||||
* wxt.hooks.hook('prepare:tsconfig', (wxt, { tsconfig }) => {
|
||||
* tsconfig.compilerOptions.lib.push('WebWorker');
|
||||
* });
|
||||
*/
|
||||
'prepare:tsconfig': (wxt: Wxt, configs: PrepareTsconfigs) => HookResult;
|
||||
/**
|
||||
* Called before generating the list of public paths inside
|
||||
* `.wxt/types/paths.d.ts`. Use this hook to add additional paths (relative to
|
||||
@@ -1530,7 +1565,12 @@ 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;
|
||||
};
|
||||
/** Chokidar options used by dev-mode file watchers. */
|
||||
watchOptions: vite.WatchOptions;
|
||||
dev: {
|
||||
/** Only defined during dev command */
|
||||
server?: {
|
||||
|
||||
Vendored
+1
-1
@@ -23,6 +23,6 @@
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@wxt-dev/module-react": "^1.1.5",
|
||||
"typescript": "^5.9.3",
|
||||
"wxt": "^0.20.26"
|
||||
"wxt": "^0.20.27"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -20,6 +20,6 @@
|
||||
"devDependencies": {
|
||||
"@wxt-dev/module-solid": "^1.1.4",
|
||||
"typescript": "^5.9.3",
|
||||
"wxt": "^0.20.26"
|
||||
"wxt": "^0.20.27"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -21,6 +21,6 @@
|
||||
"svelte-check": "^4.4.4",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.9.3",
|
||||
"wxt": "^0.20.26"
|
||||
"wxt": "^0.20.27"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -16,6 +16,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3",
|
||||
"wxt": "^0.20.26"
|
||||
"wxt": "^0.20.27"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -21,6 +21,6 @@
|
||||
"@wxt-dev/module-vue": "^1.0.3",
|
||||
"typescript": "^5.9.3",
|
||||
"vue-tsc": "^3.2.5",
|
||||
"wxt": "^0.20.26"
|
||||
"wxt": "^0.20.27"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user