Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ac87bef44 | |||
| 4caaa1bc4d | |||
| e46920b6a0 | |||
| 6d4dae59c6 | |||
| 1f3616b056 | |||
| 78a146b1ca | |||
| 8447f9aa7c | |||
| ee768f84bb | |||
| cfd4979a7a | |||
| de5b4e16cd | |||
| 5c696da6e2 | |||
| 2363144bb6 | |||
| 9e2d11d8db | |||
| f2523c778f | |||
| 04a51581fc | |||
| bb40db78e3 |
@@ -1,10 +0,0 @@
|
||||
extends:
|
||||
- '@commitlint/config-conventional'
|
||||
rules:
|
||||
subject-case:
|
||||
- 0
|
||||
- always
|
||||
- - sentence-case
|
||||
- start-case
|
||||
- pascal-case
|
||||
- upper-case
|
||||
@@ -1,17 +1,2 @@
|
||||
# See https://git-scm.com/docs/gitattributes#_pattern_format for more about `.gitattributes`.
|
||||
|
||||
# Normalize EOL for all files that Git considers text files
|
||||
* text=auto eol=lf
|
||||
|
||||
# Mark lock files as generated to avoid diffing
|
||||
pnpm-lock.yaml linguist-generated
|
||||
package-lock.json linguist-generated
|
||||
bun.lockb linguist-generated
|
||||
bun.lock linguist-generated
|
||||
yarn.lock linguist-generated
|
||||
|
||||
# Exclude templates from language statistics
|
||||
templates/**/* linguist-vendored
|
||||
|
||||
# Other generated files
|
||||
packages/browser/src/gen/** linguist-generated
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# Set default
|
||||
* @aklinker1
|
||||
|
||||
# Secure Directories
|
||||
/.github/ @aklinker1
|
||||
|
||||
# Creator of specific wxt modules
|
||||
/packages/auto-icons/ @Timeraa
|
||||
/packages/unocss/ @Timeraa
|
||||
|
||||
# Other Directories
|
||||
/docs @aklinker1 @PatrykKuniczak
|
||||
@@ -1,3 +0,0 @@
|
||||
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository#about-funding-files
|
||||
|
||||
github: wxt-dev
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report an issue with WXT
|
||||
title: ''
|
||||
labels: triage
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### Describe the bug
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
### To Reproduce
|
||||
|
||||
Upload a ZIP or share a link to a repo representing the minimal reproduction. **_If you don't upload a minimal reproduction, you bug report will be closed._**
|
||||
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
### Expected behavior
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
### Screenshots
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
### Environment
|
||||
|
||||
<!--- Run `npx envinfo --system --browsers --binaries --npmPackages wxt,vite` and paste the output below -->
|
||||
|
||||
```
|
||||
Paste output here
|
||||
```
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the problem here, otherwise you can delete this section.
|
||||
@@ -1,75 +0,0 @@
|
||||
name: "\U0001F41E Bug report"
|
||||
description: Report an issue with WXT
|
||||
labels: [pending-triage]
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
||||
placeholder: I am doing ... What I expect is ... What actually happening is ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: |
|
||||
Please provide a minimal reproduction. This can include:
|
||||
|
||||
- A PR with a failing test case
|
||||
- A link to a github repo
|
||||
- A ZIP you upload to this issue
|
||||
|
||||
A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required ([Why?](https://antfu.me/posts/why-reproductions-are-required)). If a report is vague (e.g. just a generic error message) and has no reproduction or a partial reproduction, it will be closed immediately and labeled with as "needs-reproduction". Once a reproduction is provided, it will be re-opened.
|
||||
placeholder: Reproduction URL or attach a ZIP
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please provide any reproduction steps that may need to be described. E.g. if it happens only when running the dev or build script make sure it's clear which one to use.
|
||||
placeholder: Run `npm install` followed by `npm run dev`
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Info
|
||||
description: Output of `npx envinfo --system --browsers --binaries --npmPackages wxt,vite`
|
||||
render: shell
|
||||
placeholder: System, Binaries, Browsers
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: package-manager
|
||||
attributes:
|
||||
label: Used Package Manager
|
||||
description: Select the used package manager
|
||||
options:
|
||||
- npm
|
||||
- yarn
|
||||
- pnpm
|
||||
- bun
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checkboxes
|
||||
attributes:
|
||||
label: Validations
|
||||
description: Before submitting the issue, please make sure you do the following
|
||||
options:
|
||||
- label: Read the [Contributing Guidelines](https://github.com/wxt-dev/wxt/blob/main/CONTRIBUTING.md).
|
||||
required: true
|
||||
- label: Read the [docs](https://wxt.dev/guide/installation.html).
|
||||
required: true
|
||||
- label: Check that there isn't [already an issue](https://github.com/wxt-dev/wxt/issues) that reports the same bug to avoid creating a duplicate.
|
||||
required: true
|
||||
- label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/wxt-dev/wxt/discussions) or join our [Discord Chat Server](https://discord.gg/ZFsZqGery9).
|
||||
required: true
|
||||
- label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
|
||||
required: true
|
||||
@@ -1,8 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord Chat
|
||||
url: https://discord.gg/ZFsZqGery9
|
||||
about: Ask questions and discuss with other WXT users in real time.
|
||||
- name: Questions & Discussions
|
||||
url: https://github.com/wxt-dev/wxt/discussions
|
||||
about: Use GitHub discussions for message-board style questions and discussions.
|
||||
@@ -2,7 +2,7 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for WXT
|
||||
title: ''
|
||||
type: Feature
|
||||
labels: feature
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
name: Basic Setup
|
||||
description: Install Bun and dependencies
|
||||
|
||||
inputs:
|
||||
install:
|
||||
default: 'true'
|
||||
description: Whether or not to run 'bun install'
|
||||
|
||||
description: Install PNPM, Node, and dependencies
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
steps:
|
||||
- name: 🛠️ Setup Bun
|
||||
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
bun-version-file: '.tool-versions'
|
||||
|
||||
- name: 📦 Install Dependencies
|
||||
if: ${{ inputs.install == 'true' }}
|
||||
version: 8
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: pnpm
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: bun ci ${{ inputs.installArgs }}
|
||||
run: pnpm install
|
||||
|
||||
+7
-16
@@ -1,20 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: bun
|
||||
directory: /
|
||||
- package-ecosystem: npm
|
||||
directory: '/' # Location of package manifests
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
groups:
|
||||
production-updates:
|
||||
applies-to: version-updates
|
||||
dependency-type: production
|
||||
update-types: [minor, patch]
|
||||
dev-updates:
|
||||
applies-to: version-updates
|
||||
dependency-type: development
|
||||
update-types: [minor, patch]
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
interval: 'monthly'
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
### Overview
|
||||
|
||||
<!-- Describe your changes and why you made them -->
|
||||
|
||||
### Manual Testing
|
||||
|
||||
<!-- Describe how to test your changes to make sure the PR works as intended -->
|
||||
|
||||
### Related Issue
|
||||
|
||||
<!-- If this PR is related to an issue, please link it here -->
|
||||
|
||||
This PR closes #<issue_number>
|
||||
@@ -1,115 +0,0 @@
|
||||
name: ✨ Auto-label PR
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronized, reopened]
|
||||
|
||||
jobs:
|
||||
update-pr:
|
||||
name: Update PR
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Gather Info
|
||||
id: check
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number
|
||||
});
|
||||
|
||||
// Check if PR has assignees
|
||||
const hasAssignees = pr.assignees && pr.assignees.length > 0;
|
||||
core.setOutput('has_assignees', hasAssignees);
|
||||
core.setOutput('author', pr.user.login);
|
||||
|
||||
// Get list of changed files
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number
|
||||
});
|
||||
|
||||
// Find all packages that were modified
|
||||
const packagesRegex = /^packages\/([^\/]+)\//;
|
||||
const affectedPackages = new Set();
|
||||
|
||||
for (const file of files) {
|
||||
const match = file.filename.match(packagesRegex);
|
||||
if (match) {
|
||||
affectedPackages.add(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
const labels = Array.from(affectedPackages).map(pkg => `pkg/${pkg}`);
|
||||
core.setOutput('labels', JSON.stringify(labels));
|
||||
console.log('Detected package labels:', labels);
|
||||
|
||||
// Get current labels on the PR that match pkg/* pattern
|
||||
const currentPkgLabels = pr.labels
|
||||
.map(label => label.name)
|
||||
.filter(name => name.startsWith('pkg/'));
|
||||
|
||||
core.setOutput('current_pkg_labels', JSON.stringify(currentPkgLabels));
|
||||
console.log('Current pkg labels:', currentPkgLabels);
|
||||
|
||||
- name: Sync Author
|
||||
if: steps.check.outputs.has_assignees == 'false'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
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 }}');
|
||||
} catch (err) {
|
||||
console.warn("Error assigning author:", err);
|
||||
}
|
||||
|
||||
- name: Sync Labels
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const newLabels = ${{ steps.check.outputs.labels }};
|
||||
const currentLabels = ${{ steps.check.outputs.current_pkg_labels }};
|
||||
|
||||
// Find labels to add (in newLabels but not in currentLabels)
|
||||
const labelsToAdd = newLabels.filter(label => !currentLabels.includes(label));
|
||||
|
||||
// Find labels to remove (in currentLabels but not in newLabels)
|
||||
const labelsToRemove = currentLabels.filter(label => !newLabels.includes(label));
|
||||
|
||||
// Add new labels
|
||||
if (labelsToAdd.length > 0) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
labels: labelsToAdd
|
||||
});
|
||||
console.log('Added labels:', labelsToAdd);
|
||||
}
|
||||
|
||||
// Remove obsolete labels
|
||||
for (const label of labelsToRemove) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
name: label
|
||||
});
|
||||
console.log('Removed label:', label);
|
||||
}
|
||||
|
||||
if (labelsToAdd.length === 0 && labelsToRemove.length === 0) {
|
||||
console.log('No label changes needed');
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
name: 🔔 Notify Unreleased Commits
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 20 * * 1' # Weekly at 8 PM UTC (3 PM CT)
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository_owner == 'wxt-dev'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: List Commits
|
||||
id: list-commits
|
||||
run: |
|
||||
./scripts/list-unreleased-commits.sh > summary.txt
|
||||
|
||||
- name: Discord notification
|
||||
run: |
|
||||
PAYLOAD=$(jq -n --arg content "${{ env.MESSAGE }}" '{"content": $content}')
|
||||
curl -X POST \
|
||||
-F "payload_json=${PAYLOAD}" \
|
||||
-F "file1=@summary.txt" \
|
||||
$DISCORD_WEBHOOK
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_UNRELEASED_COMMITS }}
|
||||
MESSAGE: |
|
||||
If a package needs released, please [run the workflow](<https://github.com/wxt-dev/wxt/actions/workflows/release.yml>).
|
||||
|
||||
Before running, consider:
|
||||
- Are there any breaking changes? If so, prepare a list of breaking changes and update the changelog and release notes after the release.
|
||||
- Are there any PRs open that we wait to release after they're merged?
|
||||
@@ -1,34 +0,0 @@
|
||||
name: ✨ pkg.pr.new
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Publish Test Packages
|
||||
runs-on: ubuntu-22.04
|
||||
if: ${{ github.repository == 'wxt-dev/wxt' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Build All Packages
|
||||
run: bun run buildc all
|
||||
|
||||
- name: Publish
|
||||
run: bunx pkg-pr-new publish --compact --bun './packages/*'
|
||||
|
||||
- name: DEBUG
|
||||
if: always()
|
||||
run: ls -lhA
|
||||
working-directory: packages/analytics
|
||||
@@ -1,42 +0,0 @@
|
||||
name: 🎉 PR closed
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
thank-you:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
|
||||
steps:
|
||||
- name: Log PR Author Info
|
||||
run: |
|
||||
echo "PR Author Login: ${{ github.event.pull_request.user.login }}"
|
||||
echo "PR Author ID: ${{ github.event.pull_request.user.id }}"
|
||||
echo "PR Author Type: ${{ github.event.pull_request.user.type }}"
|
||||
echo "PR Author Node ID: ${{ github.event.pull_request.user.node_id }}"
|
||||
|
||||
- name: Post Thank You Comment
|
||||
if: >-
|
||||
github.event.pull_request.user.login != 'aklinker1' &&
|
||||
github.event.pull_request.user.login != 'Timeraa' &&
|
||||
github.event.pull_request.user.login != 'PatrykKuniczak' &&
|
||||
github.event.pull_request.user.login != 'dependabot[bot]' &&
|
||||
github.event.pull_request.user.login != 'copilot-swe-agent[bot]'
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
comment: Thanks for helping make WXT better!
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: process.env.comment
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
name: 🛡️ Check PR Title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited]
|
||||
|
||||
jobs:
|
||||
lint-pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Only fetch the config file from the repository
|
||||
sparse-checkout-cone-mode: false
|
||||
sparse-checkout: .commitlintrc.yml
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --global @commitlint/config-conventional commitlint
|
||||
|
||||
- name: Check PR title with commitlint
|
||||
env:
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
HELP_URL: https://github.com/wxt-dev/wxt/blob/main/CONTRIBUTING.md#conventional-pr-titles
|
||||
run: echo "$PR_TITLE" | npx commitlint --help-url $HELP_URL
|
||||
@@ -1,4 +1,4 @@
|
||||
name: 📝 Publish Docs
|
||||
name: Publish Docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -10,38 +10,18 @@ on:
|
||||
required: true
|
||||
default: latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
# Only run if it's the upstream repository, not forks
|
||||
if: github.repository == 'wxt-dev/wxt'
|
||||
name: Publish Docs
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: https://${{ secrets.DOCKER_REGISTRY_HOSTNAME }}
|
||||
username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build docs
|
||||
run: |
|
||||
bun run docs:build
|
||||
docker build docs/.vitepress -t ${{ secrets.DOCKER_REGISTRY_HOSTNAME }}/wxt/docs:${{ github.event.inputs.tag || 'latest' }}
|
||||
|
||||
- name: Push Image
|
||||
run: docker push ${{ secrets.DOCKER_REGISTRY_HOSTNAME }}/wxt/docs:${{ github.event.inputs.tag || 'latest' }}
|
||||
|
||||
- name: Deploy
|
||||
run: 'curl -X POST -i -H "X-API-Key: ${{ secrets.DOCS_WEBHOOK_API_KEY }}" https://${{ secrets.DOCS_WEBHOOK_DOMAIN }}/api/webhook/stacks/${{ secrets.DOCS_WEBHOOK_STACK_ID }}'
|
||||
- run: pnpm docs:build
|
||||
- run: docker build docs/.vitepress -t ${{ secrets.DOCKER_REGISTRY_HOSTNAME }}/wxt/docs:${{ github.event.inputs.tag || 'latest' }}
|
||||
- run: docker push ${{ secrets.DOCKER_REGISTRY_HOSTNAME }}/wxt/docs:${{ github.event.inputs.tag || 'latest' }}
|
||||
- run: curl -X POST -i ${{ secrets.UPDATE_DOCS_WEBHOOK }}
|
||||
|
||||
@@ -1,73 +1,34 @@
|
||||
name: 🚀 Release
|
||||
name: Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
description: Package to release
|
||||
default: wxt
|
||||
type: choice
|
||||
options:
|
||||
- analytics
|
||||
- auto-icons
|
||||
- i18n
|
||||
- is-background
|
||||
- module-react
|
||||
- module-solid
|
||||
- module-svelte
|
||||
- module-vue
|
||||
- runner
|
||||
- storage
|
||||
- unocss
|
||||
- webextension-polyfill
|
||||
- wxt
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Validate
|
||||
uses: './.github/workflows/validate.yml'
|
||||
secrets: inherit
|
||||
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write # Push version changes
|
||||
id-token: write # OIDC for NPM publishing
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- validate
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ssh-key: ${{ secrets.DEPLOY_KEY }} # https://github.com/sbellone/release-workflow-example
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git config --global push.followTags true
|
||||
- uses: ./.github/actions/setup
|
||||
|
||||
- name: Bump and Tag
|
||||
run: |
|
||||
bun run scripts/bump-package-version.ts ${{ inputs.package }}
|
||||
git config user.email "github-actions@users.noreply.github.com"
|
||||
git config user.name "GitHub Actions"
|
||||
pnpx changelogen@latest --release
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
- name: Publish to NPM
|
||||
working-directory: packages/${{ inputs.package }}
|
||||
- name: NPM
|
||||
run: |
|
||||
bun pm pack
|
||||
sudo npm i -g npm@latest
|
||||
npm publish *.tgz
|
||||
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}" > ~/.npmrc
|
||||
pnpm publish
|
||||
|
||||
- name: Create GitHub release
|
||||
run: bun run scripts/create-github-release.ts ${{ inputs.package }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: GitHub Release
|
||||
run: pnpx changelogen@latest gh release --token ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,44 +1,22 @@
|
||||
name: 🔄 Sync Releases
|
||||
name: Sync Releases
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
description: Package to sync
|
||||
default: wxt
|
||||
type: choice
|
||||
options:
|
||||
- analytics
|
||||
- auto-icons
|
||||
- i18n
|
||||
- is-background
|
||||
- module-react
|
||||
- module-solid
|
||||
- module-svelte
|
||||
- module-vue
|
||||
- runner
|
||||
- storage
|
||||
- webextension-polyfill
|
||||
- wxt
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync Releases
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
installArgs: --ignore-scripts
|
||||
|
||||
- name: Sync Releases
|
||||
run: bun run scripts/sync-releases.ts ${{ inputs.package }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: 8
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: pnpm
|
||||
- run: pnpm sync-releases all --token ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
name: 🔄 Update @wxt-dev/browser
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Every day at midnight
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: 'Sync with @types/chrome'
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'wxt-dev'
|
||||
permissions:
|
||||
contents: write # Push version changes
|
||||
id-token: write # OIDC for NPM publishing
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
installArgs: --ignore-scripts
|
||||
|
||||
- name: Generate Latest Code
|
||||
working-directory: packages/browser
|
||||
run: bun run gen
|
||||
|
||||
- name: Run Checks
|
||||
working-directory: packages/browser
|
||||
run: bun run check
|
||||
|
||||
- name: Commit Changes
|
||||
id: commit
|
||||
uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
commit_message: 'fix: Upgrade `@wxt-dev/browser` to latest `@types/chrome` version'
|
||||
|
||||
- name: Publish to NPM
|
||||
if: steps.commit.outputs.changes_detected == 'true'
|
||||
working-directory: packages/browser
|
||||
run: |
|
||||
bun pm pack
|
||||
sudo npm i -g npm@latest
|
||||
npm publish *.tgz
|
||||
+49
-102
@@ -1,4 +1,4 @@
|
||||
name: 🛡️ Validate
|
||||
name: Validate
|
||||
on:
|
||||
workflow_call:
|
||||
pull_request:
|
||||
@@ -6,97 +6,57 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
checks:
|
||||
name: Checks
|
||||
formatting:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Basic Checks
|
||||
run: bun run check
|
||||
|
||||
builds:
|
||||
name: Builds
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: pnpm format:check
|
||||
lint:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Build All Packages
|
||||
run: bun run buildc all
|
||||
|
||||
build-demo:
|
||||
name: Build Demo
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: pnpm lint
|
||||
type-check:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Build
|
||||
run: bun run build:all
|
||||
working-directory: packages/wxt-demo
|
||||
|
||||
- name: ZIP
|
||||
run: bun run wxt zip
|
||||
working-directory: packages/wxt-demo
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: pnpm compile
|
||||
validate-demo:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: pnpm build:all
|
||||
working-directory: demo
|
||||
- run: pnpm tsc --noEmit
|
||||
working-directory: demo
|
||||
- run: pnpm wxt zip
|
||||
working-directory: demo
|
||||
- run: pnpm vitest run
|
||||
working-directory: demo
|
||||
tests:
|
||||
name: Tests (${{ matrix.title }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- title: 'Linux'
|
||||
os: ubuntu-22.04
|
||||
coverage: true
|
||||
- title: 'Windows'
|
||||
os: windows-latest
|
||||
coverage: false
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@739bfe42ca9233c5e6aca07c1a25a9d34aca49b0 # v6.0.7
|
||||
with:
|
||||
version: 10.33.0
|
||||
|
||||
- name: Run Tests
|
||||
if: ${{ ! matrix.coverage }}
|
||||
run: bun run test
|
||||
|
||||
- name: Run Tests (Coverage)
|
||||
if: matrix.coverage
|
||||
run: bun run test:coverage --reporter=default --reporter=hanging-process
|
||||
|
||||
- name: Upload Coverage
|
||||
if: matrix.coverage
|
||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- name: pnpm test:coverage
|
||||
run: pnpm test:coverage --reporter=default --reporter=hanging-process
|
||||
- uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
windows-tests:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: pnpm test run
|
||||
template:
|
||||
name: Template
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
template:
|
||||
- react
|
||||
@@ -105,34 +65,21 @@ jobs:
|
||||
- vanilla
|
||||
- vue
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Pack WXT package
|
||||
run: bun pm pack
|
||||
working-directory: packages/wxt
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm i
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: pnpm pack
|
||||
- run: npm i
|
||||
working-directory: templates/${{ matrix.template }}
|
||||
|
||||
- name: Install Packed WXT
|
||||
run: npm i -D ../../packages/wxt/wxt-*.tgz
|
||||
- run: npm i -D ../../wxt-*.tgz
|
||||
working-directory: templates/${{ matrix.template }}
|
||||
|
||||
- name: Type Check Template
|
||||
run: bun run compile
|
||||
- run: pnpm compile
|
||||
if: matrix.template != 'svelte'
|
||||
working-directory: templates/${{ matrix.template }}
|
||||
|
||||
- name: Type Check Template
|
||||
run: bun run check
|
||||
- run: pnpm check
|
||||
if: matrix.template == 'svelte'
|
||||
working-directory: templates/${{ matrix.template }}
|
||||
|
||||
- name: Build Template
|
||||
run: bun run build
|
||||
- run: pnpm build
|
||||
working-directory: templates/${{ matrix.template }}
|
||||
env:
|
||||
# Debug Vite 5's deprecated CJS support
|
||||
VITE_CJS_TRACE: true
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
name: 📼 VHS
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/tapes/*.tape'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
vhs:
|
||||
name: Create VHS
|
||||
runs-on: macos-latest
|
||||
if: ${{ github.repository == 'wxt-dev/wxt' }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.ref_name }}
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@739bfe42ca9233c5e6aca07c1a25a9d34aca49b0 # v6.0.7
|
||||
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
cache: pnpm
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||
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
|
||||
|
||||
- name: Record VHS
|
||||
run: |
|
||||
brew install ttyd ffmpeg
|
||||
go install github.com/charmbracelet/vhs@517bcda0faf416728bcf6b7fe489eb0e2469d9b5 # v0.10.0
|
||||
vhs docs/tapes/init-demo.tape
|
||||
|
||||
- name: Save recorded GIF
|
||||
uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
commit_message: 'docs: Update `wxt init` GIF'
|
||||
commit_user_name: github-actions[bot]
|
||||
commit_user_email: github-actions[bot]@users.noreply.github.com
|
||||
commit_author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||
# https://github.com/charmbracelet/vhs#output
|
||||
file_pattern: 'docs/assets/*.gif'
|
||||
+3
-7
@@ -1,16 +1,13 @@
|
||||
.DS_Store
|
||||
.env
|
||||
.env.*
|
||||
!packages/wxt/src/core/utils/__tests__/fixtures/.env
|
||||
.idea
|
||||
.output
|
||||
.webextrc
|
||||
.wxt
|
||||
.wxt-runner
|
||||
*.log
|
||||
*.tgz
|
||||
/.vscode/
|
||||
/docs/.vitepress/cache
|
||||
docs/.vitepress/.temp
|
||||
coverage
|
||||
dist
|
||||
node_modules
|
||||
@@ -20,7 +17,6 @@ web-ext.config.ts
|
||||
templates/*/pnpm-lock.yaml
|
||||
templates/*/yarn.lock
|
||||
templates/*/package-lock.json
|
||||
docs/api/reference
|
||||
docs/api
|
||||
stats.html
|
||||
.cache
|
||||
*-stats.txt
|
||||
.tool-versions
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint/refs/heads/main/schema/markdownlint-config-schema.json",
|
||||
"line-length": false,
|
||||
"no-inline-html": false,
|
||||
"first-line-heading": false
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
node_modules
|
||||
.git
|
||||
.output
|
||||
dist
|
||||
|
||||
# Generated files
|
||||
packages/wxt/README.md
|
||||
packages/*/CHANGELOG.md
|
||||
docs/api
|
||||
@@ -5,4 +5,3 @@ dist
|
||||
docs/.vitepress/cache
|
||||
pnpm-lock.yaml
|
||||
CHANGELOG.md
|
||||
packages/browser/src/gen
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
singleQuote: true
|
||||
endOfLine: lf
|
||||
plugins:
|
||||
- prettier-plugin-jsdoc
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
bun 1.3.13
|
||||
nodejs 24.14.1
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"davidanson.vscode-markdownlint",
|
||||
"esbenp.prettier-vscode",
|
||||
"github.vscode-github-actions"
|
||||
]
|
||||
}
|
||||
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
{
|
||||
// Set default formatter
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
|
||||
"[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"[yaml]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
"[markdown]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||
|
||||
// Additional guidelines for Copilot
|
||||
"github.copilot.chat.codeGeneration.instructions": [
|
||||
{ "file": "CONTRIBUTING.md" }
|
||||
],
|
||||
"oxc.enable": true
|
||||
}
|
||||
+1134
File diff suppressed because it is too large
Load Diff
@@ -1,128 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
<aaronklinker1@gmail.com>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
<https://www.contributor-covenant.org/faq>. Translations are available at
|
||||
<https://www.contributor-covenant.org/translations>.
|
||||
+31
-163
@@ -1,59 +1,29 @@
|
||||
# Contributing
|
||||
|
||||
Everyone is welcome to contribute to **WXT**!
|
||||
Everyone is welcome to contribute to WXT!
|
||||
|
||||
If you are changing the docs or fixing a bug, feel free to fork and open a PR.
|
||||
|
||||
If you want to add a new feature, please create an issue or discussion first so we can decide if the feature is inline with the vision for WXT.
|
||||
|
||||
## WXT's Vision
|
||||
|
||||
WXT is two things:
|
||||
|
||||
1. A build tool
|
||||
2. A set of runtime utilities
|
||||
|
||||
The long term goal of WXT is provide an opinionated build tool that keeps WXT projects standard, while providing light-weight runtime utils that simplify a lot of the boilerplate/overhead when setting up a new extension.
|
||||
|
||||
I also want to provide a way for developers to use either one of those two things independently, and not require them to use both. This is why all of WXT's runtime utils are shipped as their own NPM packages, most of them not bundled inside the core `wxt` package. If you just want to use the packages, they're available, and if you just want to use WXT's built tool, you don't have to import any of WXT's utilities, you can use your own.
|
||||
|
||||
> The few runtime utils shipped inside WXT are things that should be used by 90% of extensions. That said, they're also legacy utils left in from before I started creating separate NPM packages, and in the future, they may be removed from the core package.
|
||||
|
||||
## Conventional Commits
|
||||
|
||||
This project uses [Conventional Commit format](https://www.conventionalcommits.org/en/v1.0.0/) to automatically generate a changelog and better understand the changes in the project
|
||||
This project uses [Conventional Commits](https://www.conventionalcommits.org/en) to automate versioning. If you're a new contributor, don't worry about this. When you open a PR, a maintainer will change the PR's title so it's in the style of conventional commits, but that's all.
|
||||
|
||||
Here are some examples of conventional commit messages:
|
||||
|
||||
- `feat: add new functionality`
|
||||
- `fix: correct typos in code`
|
||||
- `ci: add GitHub Actions for automated testing`
|
||||
|
||||
## Conventional PR Titles
|
||||
|
||||
The title of your pull request should follow the [conventional commit format](#conventional-commits). When a pull request is merged to the main branch, all changes are going to be squashed into a single commit. The message of this commit will be the title of the pull request. And for every release, the commit messages are used to generate the changelog.
|
||||
|
||||
## Breaking Changes Policy
|
||||
|
||||
A quick word on WXT's breaking changes policy. I am willing to make breaking changes, but they have to be for a good enough reason - they have to make WXT better as a whole, they can't be based on one opinion.
|
||||
|
||||
Breaking changes also require a major release. Major releases have happened once or twice a year, so after you merge your PR, you'll have to wait a little bit before it is released.
|
||||
|
||||
To make a breaking change:
|
||||
|
||||
1. Make sure you're PR is targeting the `major` branch
|
||||
2. Add `!` after the conventional commit type (`fix!: ...`, `feat!: ...`, `chore!: ...`, etc) to indicate that it is a breaking change
|
||||
3. At the top of the PR, provide documentation that will inform developers about the breaking change, why it was done, and how to migrate their extension so nothing breaks.
|
||||
- This documentation will be put ["Upgrading WXT"](https://wxt.dev/guide/resources/upgrading.html) page in the docs, read through previous breaking change docs for an idea of what is required.
|
||||
Maintainers, commits to the `main` branch (either directly or via PRs) must be valid conventional commits.
|
||||
|
||||
## Setup
|
||||
|
||||
WXT uses Bun for package management and development. Install it from: <https://bun.com/>
|
||||
|
||||
Then install dependencies:
|
||||
WXT uses `pnpm`, so make sure you have it installed.
|
||||
|
||||
```sh
|
||||
bun install
|
||||
corepack enable
|
||||
```
|
||||
|
||||
Then, simply run the install command:
|
||||
|
||||
```sh
|
||||
pnpm i
|
||||
```
|
||||
|
||||
## Development
|
||||
@@ -61,93 +31,49 @@ bun install
|
||||
Here are some helpful commands:
|
||||
|
||||
```sh
|
||||
# Build WXT package and workspace dependencies
|
||||
bun run --filter wxt build
|
||||
# Build WXT package
|
||||
pnpm build
|
||||
```
|
||||
|
||||
```sh
|
||||
# Build workspace dependencies, then start the demo extension in dev mode
|
||||
bun run --filter wxt-demo dev
|
||||
# Build WXT package, then build demo extension
|
||||
cd demo
|
||||
pnpm build
|
||||
```
|
||||
|
||||
```sh
|
||||
# Build WXT package, then start the demo extension in dev mode
|
||||
cd demo
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
```sh
|
||||
# Run unit and E2E tests
|
||||
bun run test
|
||||
pnpm test
|
||||
```
|
||||
|
||||
```sh
|
||||
# Start the docs website locally
|
||||
bun run docs:dev
|
||||
pnpm docs:dev
|
||||
```
|
||||
|
||||
> Above, we used bun's `--filter` flag to choose which package to run a command in, but there are other ways:
|
||||
>
|
||||
> ```sh
|
||||
> bun run -F @wxt-dev/i18n build
|
||||
> # or
|
||||
> bun run --cwd packages/i18n build
|
||||
> # or
|
||||
> cd packages/i18n
|
||||
> bun run build
|
||||
> ```
|
||||
>
|
||||
> Pick your poison!
|
||||
|
||||
## Profiling
|
||||
|
||||
```sh
|
||||
# Build the latest version
|
||||
bun run --filter wxt build
|
||||
|
||||
# CD to the demo directory
|
||||
cd packages/wxt-demo
|
||||
```
|
||||
|
||||
Then there are a few different ways to profile WXT commands:
|
||||
|
||||
- Generate a flamechart with 0x:
|
||||
|
||||
```sh
|
||||
bunx 0x node_modules/wxt/bin/wxt.mjs build
|
||||
```
|
||||
|
||||
- Create a CPU profile:
|
||||
|
||||
```sh
|
||||
bun run --cpu-prof node_modules/wxt/bin/wxt.mjs build
|
||||
```
|
||||
|
||||
- Debug the process:
|
||||
|
||||
```sh
|
||||
bun run --inspect node_modules/wxt/bin/wxt.mjs build
|
||||
```
|
||||
|
||||
## Updating Docs
|
||||
|
||||
Documentation is written with VitePress, and is located in the `docs/` directory.
|
||||
|
||||
The API reference is generated from JSDoc comments in the source code. If there's a typo or change you want to make in there, you'll need to update the source code instead of a file in the `docs/` directory.
|
||||
|
||||
## Testing
|
||||
|
||||
WXT has unit and E2E tests. When making a change or adding a feature, make sure to update the tests or add new ones, if they exist.
|
||||
|
||||
> If they don't exist, feel free to create them, but that's a lot for a one-time contributor. A maintainer might add them to your PR though.
|
||||
WXT has unit and E2E tests. When making a change or adding a feature, make sure to update the tests or add new ones.
|
||||
|
||||
To run tests for a specific file, add the filename at the end of the test command:
|
||||
|
||||
```sh
|
||||
bun run --filter wxt test manifest-contents
|
||||
pnpm test manifest-contents
|
||||
```
|
||||
|
||||
All test (unit and E2E) for all packages are ran together via [Vitest workspaces](https://vitest.dev/guide/#workspaces-support).
|
||||
Unit and E2E tests are ran together via [Vitest workspaces](https://vitest.dev/guide/#workspaces-support).
|
||||
|
||||
If you want to manually test a change, you can modify the demo project for your test, but please don't leave those changes committed once you open a PR.
|
||||
|
||||
## Templates
|
||||
|
||||
Each directory inside `templates/` is it's own standalone project. Simply `cd` into the directory you're updating, install dependencies with `npm` (NOT `bun`), and run the relevant commands
|
||||
Each directory inside `templates/` is it's own standalone project. Simply `cd` into the directory you're updating, install dependencies with `npm` (NOT `pnpm`), and run the relevant commands
|
||||
|
||||
```sh
|
||||
cd templates/vue
|
||||
@@ -156,14 +82,14 @@ npm run dev
|
||||
npm run build
|
||||
```
|
||||
|
||||
Note that templates are hardcoded to a specific version of `wxt` from NPM, they do not use the local version. PR checks will test your PR's changes against the templates, but if you want to manually do it, update the package.json dependency:
|
||||
Note that templates are hardcoded to a specific version of `wxt` from NPM, they do not use the local version. PR checks will test your changes against the templates, but if you want to manually do it, update the package.json dependency:
|
||||
|
||||
```diff
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.2",
|
||||
"vite-plugin-solid": "^2.7.0",
|
||||
- "wxt": "^0.8.0"
|
||||
+ "wxt": "../../packages/wxt"
|
||||
+ "wxt": "../.."
|
||||
}
|
||||
```
|
||||
|
||||
@@ -174,65 +100,7 @@ Then run `npm i` again.
|
||||
To add a template, copy the vanilla template and give it a new name.
|
||||
|
||||
```sh
|
||||
cp -r templates/vanilla templates/<new-template-name>
|
||||
cp -r templates/vailla templates/<new-template-name>
|
||||
```
|
||||
|
||||
That's it. Once your template is merged, it will be available inside `wxt init` immediately. You don't need to release a new version of WXT to release a new template.
|
||||
|
||||
## Releasing Updates
|
||||
|
||||
Releases are done with GitHub actions:
|
||||
|
||||
- Use the [Release workflow](https://github.com/wxt-dev/wxt/actions/workflows/release.yml) to release a single package in the monorepo. This automatically detects the version change with conventional commits, builds and uploads the package to NPM, and creates a GitHub release.
|
||||
- Use the [Sync Releases workflow](https://github.com/wxt-dev/wxt/actions/workflows/sync-releases.yml) to sync the GitHub releases with changes to the changelog. To change a release, update the `CHANGELOG.md` file and run the workflow. It will sync the releases of a single package in the monorepo.
|
||||
|
||||
## Upgrading Dependencies
|
||||
|
||||
WXT has custom rules around what dependencies can be upgraded. Use the `scripts/upgrade-deps.ts` script to upgrade dependencies and follow these rules.
|
||||
|
||||
```sh
|
||||
bun run scripts/upgrade-deps.ts
|
||||
```
|
||||
|
||||
To see all the options, run:
|
||||
|
||||
```sh
|
||||
bun run scripts/upgrade-deps.ts --help
|
||||
```
|
||||
|
||||
## Install Unreleased Versions
|
||||
|
||||
This repo uses <https://pkg.pr.new> to publish versions of all it's packages for almost every commit. You can install them via:
|
||||
|
||||
```sh
|
||||
npm i https://pkg.pr.new/[package-name]@[ref]
|
||||
```
|
||||
|
||||
Or use one of the shorthands:
|
||||
|
||||
```sh
|
||||
# Install the latest build of `wxt` from a PR:
|
||||
npm i https://pkg.pr.new/wxt@1283
|
||||
|
||||
# Install the latest build of `@wxt-dev/module-react` on the `main` branch
|
||||
npm i https://pkg.pr.new/@wxt-dev/module-react@main
|
||||
|
||||
# Install `@wxt-dev/storage` from a specific commit:
|
||||
npm i https://pkg.pr.new/@wxt-dev/module-react@426f907
|
||||
```
|
||||
|
||||
## Blog Posts
|
||||
|
||||
Anyone is welcome to submit a blog post on <https://wxt.dev/blog>!
|
||||
|
||||
> [!NOTE]
|
||||
> Before starting on a blog post, please message Aaron on Discord or start a discussion on GitHub to get permission to write about a topic, but most topics are welcome: Major version updates, tutorials, etc.
|
||||
|
||||
- **English only**: Blog posts should be written in English. Unfortunately, our maintainers don't have the bandwidth right now to translate our docs, let alone blog posts. Sorry 😓
|
||||
- **AI**: Please only use AI to translate or proof-read your blog post. Don't generate the whole thing... We don't want to publish that.
|
||||
|
||||
## Become a Maintainer
|
||||
|
||||
If you're interested in becoming a maintainer, send an email to Aaron at <aaronklinker1@gmail.com> with your github username saying you're interested. The process is very informal, I will add you quickly if you've contributed code or answered questions and helped out the community!
|
||||
|
||||
Maintainers don't have to just write code - they can manage issues, answer questions, review PRs, organize and prioritize work - there's lots of ways for you to help out.
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
# Maintainers
|
||||
|
||||
A couple of things for you to consider before merging a PR or giving the go-ahead on a feature proposal.
|
||||
|
||||
## Be picky about new features and packages
|
||||
|
||||
We are responsible for maintaining them and fixing related bugs after the PR is merged. The community can always release their own WXT modules or packages, not everything needs to be built into WXT.
|
||||
|
||||
## Prefer standards over customization
|
||||
|
||||
Don't merge PRs that just add another way to do something, like [this one](https://github.com/wxt-dev/wxt/pull/2053#issuecomment-3857010196).
|
||||
|
||||
WXT is opinionated, if you have questions about what is WXT's opinion or we need to create a new one, create an issue and @ me to discuss.
|
||||
|
||||
## PRs should be small and targeted
|
||||
|
||||
A PR should make one change. They should not make any unrelated changes outside of accomplishing the one thing. This makes PRs easier to review and they get merged more quickly - a win-win for everyone.
|
||||
|
||||
## `@wxt-dev/*` packages are separate
|
||||
|
||||
We can't make changes to these packages assuming people are using them only with WXT. For example, I almost missed [this PR](https://github.com/wxt-dev/wxt/pull/2049#issuecomment-3861251599).
|
||||
|
||||
I want these packages to be usable on their own so if people don't like WXT's build tool, they can still use our other awesome packages.
|
||||
|
||||
## Minimize dependencies
|
||||
|
||||
I don't like how heavy lots of frameworks are. It's unavoidable to a certain extent, but if you can do something without another dependency, try to.
|
||||
|
||||
If you need a dependency, look for one with zero dependencies and that's respectable.
|
||||
|
||||
## Look at milestones
|
||||
|
||||
I've organized WXT's long term plans into [milestones](https://github.com/wxt-dev/wxt/milestones), check those out to get an idea of my priorities for some of the larger features.
|
||||
|
||||
## Require tests
|
||||
|
||||
If someone opens a PR to fix a bug but doesn't include tests, don't merge the PR. Tests are required to prevent regressions and maintain a codebase long term.
|
||||
|
||||
## Ask for reproductions for bugs
|
||||
|
||||
You don't need to triage bugs if someone doesn't give you enough information. You can always ask for a repo with a reproduction or wait for more details.
|
||||
|
||||
If there's not an easy way to reproduce a bug, you're wasting your time triaging it. Just be mindful of your own time!
|
||||
|
||||
Here's an example of how to ask for a reproduction: <https://github.com/wxt-dev/wxt/issues/2064#issuecomment-3862579110>. You could be more blunt than this.
|
||||
|
||||
## Add yourself as a code owner
|
||||
|
||||
If you want to be responsible for a specific package or directory, add yourself to the [`.github/CODEOWNERS`](https://github.com/wxt-dev/wxt/blob/main/.github/CODEOWNERS) file to get added as a reviewer to PRs automatically. You can also add yourself to the default list to be added as a reviewer on all PRs.
|
||||
|
||||
## Releasing Package Updates
|
||||
|
||||
Releases are done with GitHub actions:
|
||||
|
||||
- Use the [Release workflow](https://github.com/wxt-dev/wxt/actions/workflows/release.yml) to release a single package in the monorepo. This automatically detects the version change with conventional commits, builds and uploads the package to NPM, and creates a GitHub release.
|
||||
- Use the [Sync Releases workflow](https://github.com/wxt-dev/wxt/actions/workflows/sync-releases.yml) to sync the GitHub releases with changes to the changelog. To change a release, update the `CHANGELOG.md` file and run the workflow. It will sync the releases of a single package in the monorepo.
|
||||
|
||||
## Creating New Packages
|
||||
|
||||
Example PR: <https://github.com/wxt-dev/wxt/pull/2152>
|
||||
|
||||
1. Create the package.
|
||||
|
||||
2. Update CI workflow inputs.
|
||||
|
||||
3. Add docs page and version for "Other Packages" dropdown.
|
||||
|
||||
4. Merge the PR.
|
||||
|
||||
5. Tag the commit (look at other tags for pattern):
|
||||
|
||||
```sh
|
||||
git tag <dir-name>-v<version>
|
||||
git push --tags
|
||||
```
|
||||
|
||||
6. Publish the package to NPM:
|
||||
|
||||
```sh
|
||||
cd packages/<dir-name>
|
||||
bun publish --access public
|
||||
```
|
||||
|
||||
7. Create a basic release on GitHub mentioning the new package is available.
|
||||
|
||||
A couple of things to note:
|
||||
|
||||
- pkg.pr.new will fail on the original PR. It's fine to ignore and merge your PR as long as it fails due to your new package not being published to NPM yet.
|
||||
- The regular release workflow DOES NOT WORK for new packages. You have to have at least one `<dir-name>-v<version>` tag created before you can run that workflow for your new package.
|
||||
- You don't need to create a CHANGELOG.md file for the package, it will be created automatically after future changes are released via the normal release workflow.
|
||||
@@ -1,41 +1,49 @@
|
||||
<div align="center">
|
||||
<h1 align="center">
|
||||
<img style="vertical-align:middle" width="44" src="./docs/public/hero-logo.svg" alt="WXT Logo">
|
||||
<span>WXT</span>
|
||||
</h1>
|
||||
|
||||
# <img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo"> WXT
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048"></a>
|
||||
<span> </span>
|
||||
<a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm" src="https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048"></a>
|
||||
<span> </span>
|
||||
<a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE" target="_blank"><img alt="NPM" src="https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048"></a>
|
||||
<!-- Hide code coverage while it's broken -->
|
||||
<!-- <span> </span>
|
||||
<a href="https://codecov.io/github/wxt-dev/wxt" target="_blank"><img alt="Codecov" src="https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048"></a> -->
|
||||
</p>
|
||||
|
||||
[](https://www.npmjs.com/package/wxt)
|
||||
[](https://www.npmjs.com/package/wxt)
|
||||
[](https://github.com/wxt-dev/wxt/blob/main/LICENSE)
|
||||
[](https://codecov.io/github/wxt-dev/wxt)
|
||||
<p align="center">
|
||||
<span>Next-gen framework for developing web extensions.</span>
|
||||
<br/>
|
||||
<span>⚡</span>
|
||||
<br/>
|
||||
<q><i>It's like Nuxt, but for Chrome Extensions</i></q>
|
||||
</p>
|
||||
|
||||
Next-gen framework for developing web extensions.<br/>⚡<br/><q><i>It's like Nuxt, but for Web Extensions</i></q>
|
||||
<p align="center">
|
||||
<a href="https://wxt.dev" target="_blank">Get Started</a>
|
||||
•
|
||||
<a href="https://wxt.dev/guide/installation.html" target="_blank">Installation</a>
|
||||
•
|
||||
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
|
||||
•
|
||||
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
|
||||
</p>
|
||||
|
||||
[Get Started](https://wxt.dev/guide/installation.html) •
|
||||
[Configuration](https://wxt.dev/api/config.html) •
|
||||
[Examples](https://wxt.dev/examples.html) •
|
||||
[Changelog](https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md) •
|
||||
[Discord](https://discord.gg/ZFsZqGery9)
|
||||
|
||||
</div>
|
||||
|
||||

|
||||

|
||||
|
||||
## Demo
|
||||
|
||||
<https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94>
|
||||
https://github.com/wxt-dev/wxt/assets/10101283/07359e53-f491-43b6-8e8f-fae94aec8063
|
||||
|
||||
## Quick Start
|
||||
|
||||
Bootstrap a new project:
|
||||
|
||||
```sh
|
||||
# npm
|
||||
npx wxt@latest init
|
||||
|
||||
# pnpm
|
||||
pnpm dlx wxt@latest init
|
||||
|
||||
# bun
|
||||
bunx wxt@latest init
|
||||
pnpx wxt@latest init <project-name>
|
||||
```
|
||||
|
||||
Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
|
||||
@@ -48,22 +56,17 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
|
||||
- 📂 File based entrypoints
|
||||
- 🚔 TypeScript
|
||||
- 🦾 Auto-imports
|
||||
- 🤖 Automated publishing
|
||||
- ⬇️ Download and bundle remote URL imports
|
||||
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
|
||||
- 📦 [Module system](https://wxt.dev/guide/essentials/wxt-modules.html#overview) for reusing code between extensions
|
||||
- 🖍️ Quickly bootstrap a new project
|
||||
- 📏 Bundle analysis
|
||||
- ⬇️ Download and bundle remote URL imports
|
||||
|
||||
## Sponsors
|
||||
### Coming Soon
|
||||
|
||||
WXT is a [MIT-licensed](https://github.com/wxt-dev/wxt/blob/main/LICENSE) open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring WXT's development](https://github.com/sponsors/wxt-dev).
|
||||
|
||||
[](https://github.com/sponsors/wxt-dev)
|
||||
- 🤖 Automated publishing
|
||||
|
||||
## Contributors
|
||||
|
||||
Published under the [MIT](https://github.com/wxt-dev/wxt/blob/main/LICENSE) license.
|
||||
Made by [@aklinker1](https://github.com/aklinker1) and [community](https://github.com/wxt-dev/wxt/graphs/contributors) 💛
|
||||
|
||||
[](https://github.com/wxt-dev/wxt/graphs/contributors)
|
||||
<a href="https://github.com/wxt-dev/wxt/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=wxt-dev/wxt" />
|
||||
</a>
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
While WXT is in prerelease, only the latest version will receive security updates. The latest version is:
|
||||
|
||||
<img alt="npm version" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048">
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a security vulnerability, please email me at <aaronklinker1@gmail.com>. I will respond within a few days to acknowledge receipt of your report.
|
||||
|
||||
If the vulnerability is accepted, I will open a public issue to track the fix. If the vulnerability is not accepted, no further action will be taken.
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
import '../dist/cli.js';
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
version: '0.2'
|
||||
ignorePaths:
|
||||
- pnpm-lock.yaml
|
||||
- '**/node_modules/**'
|
||||
- '**/dist/**'
|
||||
- '**/coverage/**'
|
||||
- docs/api/**
|
||||
- '**/CHANGELOG.md'
|
||||
- docs/public/_redirects
|
||||
- packages/browser/src/gen/**
|
||||
- patches/**
|
||||
- '**/*.snap'
|
||||
- docs/assets/extension-showcase.yml
|
||||
- packages/*/stats.html
|
||||
words:
|
||||
- Aabid
|
||||
- aabidk
|
||||
- aklinker
|
||||
- automount
|
||||
- avenir
|
||||
- bidi
|
||||
- buildc
|
||||
- bunx
|
||||
- cachable
|
||||
- cacjs
|
||||
- charmbracelet
|
||||
- chromedriver
|
||||
- Cira
|
||||
- comctx
|
||||
- confbox
|
||||
- configfile
|
||||
- consolas
|
||||
- Cpath
|
||||
- crbug
|
||||
- crunchyroll
|
||||
- cssinjs
|
||||
- CSUI
|
||||
- Csvg
|
||||
- defu
|
||||
- deinit
|
||||
- dequal
|
||||
- dtrace
|
||||
- duckduckgo
|
||||
- esbuild
|
||||
- esque
|
||||
- extglob
|
||||
- favicons
|
||||
- fieldtrials
|
||||
- firefoxdeveloperedition
|
||||
- flamechart
|
||||
- Florian
|
||||
- focusring
|
||||
- frontmatter
|
||||
- giget
|
||||
- grayscale
|
||||
- greyscale
|
||||
- gtag
|
||||
- hckhakegfgenefhikdcfkaaonnclljmf
|
||||
- hookable
|
||||
- iconify
|
||||
- jsons
|
||||
- Kenobi
|
||||
- keystyle
|
||||
- klinker
|
||||
- linkedom
|
||||
- llms
|
||||
- locationchange
|
||||
- magicast
|
||||
- Menlo
|
||||
- menupanel
|
||||
- metas
|
||||
- mktemp
|
||||
- mycompany
|
||||
- nacl
|
||||
- nanospinner
|
||||
- natsu
|
||||
- newtab
|
||||
- njsproj
|
||||
- nodebuffer
|
||||
- Noto
|
||||
- ntvs
|
||||
- nypm
|
||||
- obug
|
||||
- ohash
|
||||
- oklab
|
||||
- oxlint
|
||||
- pbbf
|
||||
- personaltoolbar
|
||||
- portaling
|
||||
- prebundled
|
||||
- prefs
|
||||
- proxified
|
||||
- pWXLO
|
||||
- repost
|
||||
- rxliuli
|
||||
- sandboxed
|
||||
- scule
|
||||
- Segoe
|
||||
- sidepanel
|
||||
- sidepanels
|
||||
- solidjs
|
||||
- styl
|
||||
- subdependency
|
||||
- tabstrip
|
||||
- teleporting
|
||||
- treemap
|
||||
- triaging
|
||||
- umami
|
||||
- ungh
|
||||
- unimport
|
||||
- unocss
|
||||
- unrun
|
||||
- unstub
|
||||
- unwatch
|
||||
- USSS
|
||||
- vitepress
|
||||
- webext
|
||||
- webextension
|
||||
- webextrc
|
||||
- xcrun
|
||||
- xlink
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "demo",
|
||||
"version": "1.0.0",
|
||||
"description": "Demo extension for WXT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "pnpm -w build && wxt",
|
||||
"build": "pnpm -w build && wxt build",
|
||||
"build:all": "pnpm -w build && run-s -s 'build:all:*'",
|
||||
"build:all:chrome-mv3": "wxt build",
|
||||
"build:all:chrome-mv2": "wxt build --mv2",
|
||||
"build:all:firefox-mv3": "wxt build -b firefox --mv3",
|
||||
"build:all:firefox-mv2": "wxt build -b firefox",
|
||||
"test": "pnpm -w build && vitest",
|
||||
"zip": "pnpm -w build && wxt zip",
|
||||
"compile": "pnpm -w build && tsc --noEmit",
|
||||
"postinstall": "pnpm -w build && wxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"vitest": "^0.34.6",
|
||||
"webextension-polyfill": "^0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.34",
|
||||
"@types/react-dom": "^18.2.14",
|
||||
"@types/webextension-polyfill": "^0.10.5",
|
||||
"sass": "^1.69.5",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import background from '../background';
|
||||
|
||||
browser.i18n.getMessage = () => 'fake-message';
|
||||
|
||||
const logMock = vi.fn();
|
||||
console.log = logMock;
|
||||
|
||||
describe('Background Entrypoint', () => {
|
||||
it("should log the extenion's runtime ID", () => {
|
||||
const id = 'some-id';
|
||||
fakeBrowser.runtime.id = id;
|
||||
|
||||
background.main();
|
||||
|
||||
expect(logMock).toBeCalledWith(id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import messages from 'public/_locales/en/messages.json';
|
||||
|
||||
export default defineBackground(() => {
|
||||
console.log(browser.runtime.id);
|
||||
logId();
|
||||
console.log({
|
||||
browser: __BROWSER__,
|
||||
chrome: __IS_CHROME__,
|
||||
firefox: __IS_FIREFOX__,
|
||||
manifestVersion: __MANIFEST_VERSION__,
|
||||
messages,
|
||||
});
|
||||
|
||||
// @ts-expect-error: should only accept entrypoints or public assets
|
||||
browser.runtime.getURL('/');
|
||||
browser.runtime.getURL('/background.js');
|
||||
browser.runtime.getURL('/icon/128.png');
|
||||
|
||||
// @ts-expect-error: should only accept known message names
|
||||
browser.i18n.getMessage('test');
|
||||
browser.i18n.getMessage('prompt_for_name');
|
||||
browser.i18n.getMessage('hello', 'Aaron');
|
||||
browser.i18n.getMessage('bye', ['Aaron']);
|
||||
browser.i18n.getMessage('@@extension_id');
|
||||
|
||||
console.log('WXT MODE:', {
|
||||
MODE: import.meta.env.MODE,
|
||||
DEV: import.meta.env.DEV,
|
||||
PROD: import.meta.env.PROD,
|
||||
});
|
||||
|
||||
storage.setItem('session:startTime', Date.now());
|
||||
});
|
||||
+2
-2
@@ -2,9 +2,9 @@ export default defineContentScript({
|
||||
matches: ['*://*.google.com/*'],
|
||||
|
||||
main(ctx) {
|
||||
const ui = createIframeUi(ctx, {
|
||||
const ui = createContentScriptIframe(ctx, {
|
||||
page: '/iframe-src.html',
|
||||
position: 'overlay',
|
||||
type: 'overlay',
|
||||
anchor: 'form[action="/search"]',
|
||||
});
|
||||
ui.mount();
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// @ts-expect-error: URL imports not typed
|
||||
import 'url:https://code.jquery.com/jquery-3.7.1.slim.min.js';
|
||||
|
||||
console.log(browser.runtime.id);
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Popup</title>
|
||||
<link rel="stylesheet" href="uno.css" />
|
||||
<link rel="stylesheet" href="../common/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello popup!</p>
|
||||
@@ -0,0 +1,24 @@
|
||||
import '../../common/style.css';
|
||||
import './style.css';
|
||||
|
||||
export default defineContentScript({
|
||||
matches: ['https://*.duckduckgo.com/*'],
|
||||
cssInjectionMode: 'ui',
|
||||
|
||||
async main(ctx) {
|
||||
const ui = await createContentScriptUi(ctx, {
|
||||
name: 'demo-ui',
|
||||
type: 'inline',
|
||||
append: 'before',
|
||||
anchor: 'form[role=search]',
|
||||
mount: (container) => {
|
||||
const app = document.createElement('div');
|
||||
app.textContent = 'Custom content script UI';
|
||||
container.append(app);
|
||||
},
|
||||
});
|
||||
ui.mount();
|
||||
|
||||
setTimeout(ui.remove, 5000);
|
||||
},
|
||||
});
|
||||
+5
@@ -1,6 +1,11 @@
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
color: indianred;
|
||||
}
|
||||
html {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
div {
|
||||
padding: 16px;
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export default defineUnlistedScript(() => {
|
||||
console.log('Injected');
|
||||
console.log('injected');
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"prompt_for_name": {
|
||||
"message": "What's your name?",
|
||||
"description": "Ask for the user's name"
|
||||
},
|
||||
"hello": {
|
||||
"message": "Hello, $USER$",
|
||||
"description": "Greet the user",
|
||||
"placeholders": {
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "Cira"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bye": {
|
||||
"message": "Goodbye, $USER$. Come back to $OUR_SITE$ soon!",
|
||||
"description": "Say goodbye to the user",
|
||||
"placeholders": {
|
||||
"our_site": {
|
||||
"content": "Example.com"
|
||||
},
|
||||
"user": {
|
||||
"content": "$1",
|
||||
"example": "Cira"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 504 B |
Binary file not shown.
|
After Width: | Height: | Size: 936 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": ["../tsconfig.base.json", "./.wxt/tsconfig.json"],
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": true,
|
||||
"jsx": "react-jsx"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import { WxtVitest } from 'wxt/testing';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
mockReset: true,
|
||||
restoreMocks: true,
|
||||
},
|
||||
plugins: [WxtVitest()],
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { defineConfig } from 'wxt';
|
||||
|
||||
export default defineConfig({
|
||||
srcDir: 'src',
|
||||
manifest: {
|
||||
permissions: ['storage'],
|
||||
default_locale: 'en',
|
||||
web_accessible_resources: [
|
||||
{
|
||||
resources: ['/iframe-src.html'],
|
||||
matches: ['*://*.google.com/*'],
|
||||
},
|
||||
],
|
||||
},
|
||||
alias: {
|
||||
public: 'src/public',
|
||||
},
|
||||
});
|
||||
@@ -1,71 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
// @ts-expect-error: Vitepress data-loader magic, this import is correct
|
||||
import { data } from '../loaders/blog.data';
|
||||
import BlogPostPreview from './BlogPostPreview.vue';
|
||||
import { Post } from '../utils/types';
|
||||
|
||||
const posts = computed<Post[]>(() =>
|
||||
data
|
||||
.map((post) => ({
|
||||
...post,
|
||||
...post.frontmatter,
|
||||
date: new Date(post.frontmatter.date),
|
||||
}))
|
||||
.sort((a, b) => b.date.getTime() - a.date.getTime()),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div>
|
||||
<div class="vp-doc">
|
||||
<h1>Blog</h1>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<BlogPostPreview v-for="post of posts" :key="post.url" :post />
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.container > div {
|
||||
padding: 32px;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
list-style: none;
|
||||
}
|
||||
ul,
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
</style>
|
||||
@@ -1,77 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import useBlogDate from '../composables/useBlogDate';
|
||||
import { Content, useData } from 'vitepress';
|
||||
import { PostFrontmatter } from '../utils/types';
|
||||
|
||||
const { frontmatter } = useData<PostFrontmatter>();
|
||||
const date = useBlogDate(() => frontmatter.value.date);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-doc">
|
||||
<main class="container-content">
|
||||
<h1 v-html="frontmatter.title" />
|
||||
<p class="meta-row">
|
||||
<a
|
||||
v-for="author of frontmatter.authors"
|
||||
:key="author.github"
|
||||
:href="`https://github.com/${author.github}`"
|
||||
class="author"
|
||||
>
|
||||
<img :src="`https://github.com/${author.github}.png?size=96`" />
|
||||
<span>{{ author.name }}</span>
|
||||
</a>
|
||||
<span>•</span>
|
||||
<span>{{ date }}</span>
|
||||
</p>
|
||||
<Content />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
vp-doc {
|
||||
display: flex;
|
||||
}
|
||||
main {
|
||||
max-width: 1080px;
|
||||
padding: 32px;
|
||||
margin: auto;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
main {
|
||||
padding: 64px;
|
||||
}
|
||||
}
|
||||
.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>
|
||||
@@ -1,72 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import useBlogDate from '../composables/useBlogDate';
|
||||
|
||||
const props = defineProps<{
|
||||
post: {
|
||||
title: string;
|
||||
description?: string;
|
||||
date: Date;
|
||||
url: string;
|
||||
authors: Array<{ name: string; github: string }>;
|
||||
};
|
||||
}>();
|
||||
|
||||
const date = useBlogDate(() => props.post.date);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li class="blog-list-item">
|
||||
<a :href="post.url">
|
||||
<div class="vp-doc">
|
||||
<h3 class="title" v-html="post.title" />
|
||||
<p class="description" v-html="post.description" />
|
||||
<p class="meta">
|
||||
{{ post.authors.map((author) => author.name).join(', ') }}
|
||||
•
|
||||
{{ date }}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
patterns: Array<[intput: string, output: string]>;
|
||||
}>();
|
||||
</script>
|
||||
@@ -8,14 +8,14 @@ defineProps<{
|
||||
<table class="no-vertical-dividers">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 100%">Filename</th>
|
||||
<th>Input Pattern</th>
|
||||
<th></th>
|
||||
<th>Output Path</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="pattern of patterns">
|
||||
<td style="white-space: nowrap; padding-right: 8px">
|
||||
<td style="white-space: nowrap">
|
||||
<code>entrypoints/{{ pattern[0] }}</code>
|
||||
</td>
|
||||
<td style="padding: 6px; opacity: 50%">
|
||||
@@ -31,7 +31,7 @@ defineProps<{
|
||||
/>
|
||||
</svg>
|
||||
</td>
|
||||
<td style="white-space: nowrap; padding-left: 8px">
|
||||
<td style="white-space: nowrap">
|
||||
<code>/{{ pattern[1] }}</code>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, Ref, toRaw } from 'vue';
|
||||
import ExampleSearchFilterByItem from './ExampleSearchFilterByItem.vue';
|
||||
import ExampleSearchResult from './ExampleSearchResult.vue';
|
||||
import { ExamplesMetadata, KeySelectedObject } from '../utils/types';
|
||||
|
||||
defineProps<{
|
||||
tag?: string;
|
||||
}>();
|
||||
|
||||
const exampleMetadata = ref<ExamplesMetadata>();
|
||||
onMounted(async () => {
|
||||
const res = await fetch(
|
||||
'https://raw.githubusercontent.com/wxt-dev/examples/main/metadata.json',
|
||||
);
|
||||
exampleMetadata.value = await res.json();
|
||||
});
|
||||
|
||||
const searchText = ref('');
|
||||
const selectedApis = ref<KeySelectedObject>({});
|
||||
const selectedPermissions = ref<KeySelectedObject>({});
|
||||
const selectedPackages = ref<KeySelectedObject>({});
|
||||
|
||||
function useRequiredItems(selectedItems: Ref<KeySelectedObject>) {
|
||||
return computed(() =>
|
||||
Array.from(
|
||||
Object.entries(toRaw(selectedItems.value)).reduce(
|
||||
(set, [pkg, checked]) => {
|
||||
if (checked) set.add(pkg);
|
||||
return set;
|
||||
},
|
||||
new Set<string>(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
const requiredApis = useRequiredItems(selectedApis);
|
||||
const requiredPermissions = useRequiredItems(selectedPermissions);
|
||||
const requiredPackages = useRequiredItems(selectedPackages);
|
||||
|
||||
function doesExampleMatchSelected(
|
||||
exampleItems: string[],
|
||||
requiredItems: Ref<string[]>,
|
||||
) {
|
||||
const exampleItemsSet = new Set(exampleItems);
|
||||
return !requiredItems.value.find((item) => !exampleItemsSet.has(item));
|
||||
}
|
||||
|
||||
const filteredExamples = computed(() => {
|
||||
const text = searchText.value.toLowerCase();
|
||||
return exampleMetadata.value?.examples.filter((example) => {
|
||||
const matchesText = example.searchText.toLowerCase().includes(text);
|
||||
const matchesApis = doesExampleMatchSelected(example.apis, requiredApis);
|
||||
const matchesPermissions = doesExampleMatchSelected(
|
||||
example.permissions,
|
||||
requiredPermissions,
|
||||
);
|
||||
const matchesPackages = doesExampleMatchSelected(
|
||||
example.packages,
|
||||
requiredPackages,
|
||||
);
|
||||
return matchesText && matchesApis && matchesPermissions && matchesPackages;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="example-layout">
|
||||
<div class="search">
|
||||
<input v-model="searchText" placeholder="Search for an example..." />
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<ExampleSearchFilterByItem
|
||||
label="APIs"
|
||||
:items="exampleMetadata?.allApis"
|
||||
v-model="selectedApis"
|
||||
/>
|
||||
<ExampleSearchFilterByItem
|
||||
label="Permissions"
|
||||
:items="exampleMetadata?.allPermissions"
|
||||
v-model="selectedPermissions"
|
||||
/>
|
||||
<ExampleSearchFilterByItem
|
||||
label="Packages"
|
||||
:items="exampleMetadata?.allPackages"
|
||||
v-model="selectedPackages"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="results">
|
||||
<p v-if="exampleMetadata == null">Loading examples...</p>
|
||||
<template v-else>
|
||||
<ul class="search-results">
|
||||
<ExampleSearchResult
|
||||
v-for="example of filteredExamples"
|
||||
:key="example.name"
|
||||
:example
|
||||
/>
|
||||
</ul>
|
||||
<p v-if="filteredExamples.length === 0">No matching examples</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.example-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas:
|
||||
'search'
|
||||
'results';
|
||||
gap: 16px;
|
||||
}
|
||||
@media only screen and (min-width: 720px) {
|
||||
.example-layout {
|
||||
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) {
|
||||
.filters {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
.results {
|
||||
grid-area: results;
|
||||
}
|
||||
|
||||
.box {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.search input {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.checkbox-col {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
max-height: 200px;
|
||||
font-size: 14px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 1024px) {
|
||||
.search-results {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
@@ -1,100 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRaw } from 'vue';
|
||||
import { KeySelectedObject } from '../utils/types';
|
||||
|
||||
defineProps<{
|
||||
label: string;
|
||||
items?: string[];
|
||||
}>();
|
||||
|
||||
const selectedItems = defineModel<KeySelectedObject>({
|
||||
required: true,
|
||||
});
|
||||
|
||||
const count = computed(() => {
|
||||
return Object.values(toRaw(selectedItems.value)).filter(Boolean).length;
|
||||
});
|
||||
|
||||
function toggleItem(pkg: string) {
|
||||
selectedItems.value = {
|
||||
...toRaw(selectedItems.value),
|
||||
[pkg]: !selectedItems.value[pkg],
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="filter-container">
|
||||
<p class="header">
|
||||
<span>Filter by {{ label }}</span> <span v-if="count">({{ count }})</span>
|
||||
</p>
|
||||
<div class="scroll-container">
|
||||
<ul>
|
||||
<li v-for="item in items">
|
||||
<label :title="item">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="selectedItems[item]"
|
||||
@input="toggleItem(item)"
|
||||
/>
|
||||
<span>{{ item }}</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.filter-container {
|
||||
height: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
</style>
|
||||
@@ -1,72 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Example } from '../utils/types';
|
||||
|
||||
defineProps<{
|
||||
example: Example;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li>
|
||||
<a :href="example.url" target="_blank">
|
||||
<p class="name">{{ example.name }}</p>
|
||||
<p class="description">{{ example.description }}</p>
|
||||
<p class="link">Open →</p>
|
||||
</a>
|
||||
</li>
|
||||
</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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
a:hover .link {
|
||||
opacity: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,171 +1,116 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import useListExtensionDetails from '../composables/useListExtensionDetails';
|
||||
import _extensionEntries from '../../assets/extension-showcase.yml';
|
||||
import useListExtensionDetails, {
|
||||
ChromeExtension,
|
||||
} from '../composables/useListExtensionDetails';
|
||||
|
||||
interface StoreLink {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface ListedExtension {
|
||||
id: string;
|
||||
name: string;
|
||||
iconUrl: string;
|
||||
shortDescription: string;
|
||||
users: number;
|
||||
rating: number | undefined;
|
||||
stores: StoreLink[];
|
||||
}
|
||||
|
||||
const extensionEntries = _extensionEntries as Array<{
|
||||
chromeId?: string;
|
||||
firefoxSlug?: string;
|
||||
}>;
|
||||
|
||||
const chromeIds = extensionEntries.flatMap((e) =>
|
||||
e.chromeId ? [e.chromeId] : [],
|
||||
);
|
||||
const firefoxSlugs = [
|
||||
...new Set(
|
||||
extensionEntries.flatMap((e) => (e.firefoxSlug ? [e.firefoxSlug] : [])),
|
||||
),
|
||||
// Add extension IDs here. Order doesn't matter, will be sorted by weekly active users
|
||||
const chromeExtensionIds = [
|
||||
'ocfdgncpifmegplaglcnglhioflaimkd', // GitHub: Better Line Counts
|
||||
'mgmdkjcljneegjfajchedjpdhbadklcf', // Anime Skip Player
|
||||
'bfbnagnphiehemkdgmmficmjfddgfhpl', // UltraWideo
|
||||
'elfaihghhjjoknimpccccmkioofjjfkf', // StayFree - Website Blocker & Web Analytics
|
||||
'okifoaikfmpfcamplcfjkpdnhfodpkil', // Doozy: Ai Made Easy
|
||||
'lknmjhcajhfbbglglccadlfdjbaiifig', // tl;dv - Record, Transcribe & ChatGPT for Google Meet
|
||||
];
|
||||
|
||||
const { data, isLoading } = useListExtensionDetails(chromeIds, firefoxSlugs);
|
||||
const { data } = useListExtensionDetails(chromeExtensionIds);
|
||||
const sortedExtensions = computed(() => {
|
||||
if (!data.value?.length) return [];
|
||||
|
||||
function addUtmSource(storeUrl: string): string {
|
||||
const url = new URL(storeUrl);
|
||||
url.searchParams.set('utm_source', 'wxt.dev');
|
||||
return url.href;
|
||||
}
|
||||
|
||||
const sortedExtensions = computed((): ListedExtension[] => {
|
||||
if (!data.value) return [];
|
||||
|
||||
const chromeById = new Map(
|
||||
(data.value.chrome ?? []).filter(Boolean).map((e) => [e.id, e]),
|
||||
);
|
||||
const firefoxBySlug = new Map(
|
||||
(data.value.firefox ?? []).filter(Boolean).map((e) => [e.id, e]),
|
||||
);
|
||||
|
||||
const results: ListedExtension[] = [];
|
||||
|
||||
for (const entry of extensionEntries) {
|
||||
const chrome = entry.chromeId ? chromeById.get(entry.chromeId) : undefined;
|
||||
const firefox = entry.firefoxSlug
|
||||
? firefoxBySlug.get(entry.firefoxSlug)
|
||||
: undefined;
|
||||
|
||||
if (!chrome && !firefox) continue;
|
||||
|
||||
const primary = chrome ?? firefox!;
|
||||
const users = (chrome?.users ?? 0) + (firefox?.users ?? 0);
|
||||
const rating = chrome?.rating ?? firefox?.rating;
|
||||
|
||||
const stores: StoreLink[] = [];
|
||||
if (chrome) {
|
||||
stores.push({ label: 'Chrome', url: addUtmSource(chrome.storeUrl) });
|
||||
}
|
||||
if (firefox) {
|
||||
stores.push({ label: 'Firefox', url: addUtmSource(firefox.storeUrl) });
|
||||
}
|
||||
|
||||
results.push({
|
||||
id: entry.chromeId ?? `firefox:${entry.firefoxSlug}`,
|
||||
name: primary.name,
|
||||
iconUrl: primary.iconUrl,
|
||||
shortDescription: primary.shortDescription,
|
||||
users,
|
||||
rating,
|
||||
stores,
|
||||
});
|
||||
}
|
||||
|
||||
const score = (e: ListedExtension) => ((e.rating ?? 5) / 5) * e.users;
|
||||
return results.sort((a, b) => score(b) - score(a));
|
||||
return [...data.value]
|
||||
.map((item) => ({
|
||||
...item,
|
||||
// Sort based on the user count weighted by the rating
|
||||
sortKey: ((item.rating ?? 5) / 5) * item.weeklyActiveUsers,
|
||||
}))
|
||||
.sort((l, r) => r.sortKey - l.sortKey);
|
||||
});
|
||||
|
||||
function formatUsers(n: number): string {
|
||||
return `${n.toLocaleString()} users`;
|
||||
}
|
||||
|
||||
function formatStars(r: number): string {
|
||||
const rounded = Math.round(r * 10) / 10;
|
||||
return `${rounded % 1 === 0 ? rounded.toFixed(0) : rounded.toFixed(1)} stars`;
|
||||
function getStoreUrl(extension: ChromeExtension) {
|
||||
const url = new URL(extension.storeUrl);
|
||||
url.searchParams.set('utm_source', 'wxt.dev');
|
||||
return url.href;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p v-if="isLoading" style="text-align: center; opacity: 50%">Loading...</p>
|
||||
<p
|
||||
v-else-if="sortedExtensions.length === 0"
|
||||
style="text-align: center; opacity: 50%"
|
||||
>
|
||||
Failed to load extension details.
|
||||
</p>
|
||||
<ul v-else class="extension-grid">
|
||||
<li v-for="extension of sortedExtensions" :key="extension.id">
|
||||
<img
|
||||
:src="extension.iconUrl"
|
||||
:alt="`${extension.name} icon`"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<div class="card-content">
|
||||
<a
|
||||
:href="extension.stores[0]?.url ?? '#'"
|
||||
target="_blank"
|
||||
:title="extension.name"
|
||||
class="extension-name"
|
||||
>{{ extension.name }}</a
|
||||
<section class="vp-doc">
|
||||
<div class="container">
|
||||
<h2 id="whos-using-wxt">Who's Using WXT?</h2>
|
||||
<p>
|
||||
Battle tested and ready for production. Explore chrome extensions made
|
||||
with WXT.
|
||||
</p>
|
||||
<ul>
|
||||
<li
|
||||
v-for="extension of sortedExtensions"
|
||||
:key="extension.id"
|
||||
class="relative"
|
||||
>
|
||||
<p class="description" :title="extension.shortDescription">
|
||||
{{ extension.shortDescription }}
|
||||
</p>
|
||||
<div v-if="extension.stores.length > 0" class="store-stats">
|
||||
<span 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">
|
||||
<img
|
||||
:src="extension.iconUrl"
|
||||
:alt="`${extension.name} icon`"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<div class="relative">
|
||||
<a
|
||||
v-for="(store, i) of extension.stores"
|
||||
:key="store.label"
|
||||
:href="store.url"
|
||||
:href="getStoreUrl(extension)"
|
||||
target="_blank"
|
||||
class="store-link"
|
||||
:title="store.label"
|
||||
:title="extension.name"
|
||||
class="extension-name"
|
||||
>{{ extension.name }}</a
|
||||
>
|
||||
{{ store.label
|
||||
}}<span
|
||||
v-if="i < extension.stores.length - 1"
|
||||
class="store-stats-sep"
|
||||
aria-hidden="true"
|
||||
>, </span
|
||||
>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="centered pr">
|
||||
<a
|
||||
href="https://github.com/wxt-dev/wxt/edit/main/docs/assets/extension-showcase.yml"
|
||||
target="_blank"
|
||||
>Open a PR</a
|
||||
>
|
||||
to add your extension to the list!
|
||||
</p>
|
||||
<p class="description" :title="extension.shortDescription">
|
||||
{{ extension.shortDescription }}
|
||||
</p>
|
||||
</div>
|
||||
<p class="user-count">
|
||||
<span>{{ extension.weeklyActiveUsers.toLocaleString() }} users</span
|
||||
><template v-if="extension.rating != null"
|
||||
>,
|
||||
<span>{{ extension.rating }} stars</span>
|
||||
</template>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="centered pr">
|
||||
<a
|
||||
href="https://github.com/wxt-dev/wxt/edit/main/docs/.vitepress/components/UsingWxtSection.vue"
|
||||
target="_blank"
|
||||
>Open a PR</a
|
||||
>
|
||||
to add your extension to the list!
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.extension-grid > li > img {
|
||||
.vp-doc {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.vp-doc {
|
||||
padding: 0 48px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-doc {
|
||||
padding: 0 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1152px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
li img {
|
||||
width: 116px;
|
||||
height: 116px;
|
||||
padding: 16px;
|
||||
@@ -173,7 +118,7 @@ function formatStars(r: number): string {
|
||||
background-color: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
.extension-grid {
|
||||
ul {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
align-items: stretch;
|
||||
@@ -183,12 +128,12 @@ function formatStars(r: number): string {
|
||||
padding: 0;
|
||||
}
|
||||
@media (min-width: 960px) {
|
||||
.extension-grid {
|
||||
ul {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.extension-grid > li {
|
||||
li {
|
||||
margin: 0 !important;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
@@ -196,68 +141,28 @@ function formatStars(r: number): string {
|
||||
border-radius: 12px;
|
||||
flex: 1;
|
||||
gap: 24px;
|
||||
align-items: stretch;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extension-grid > li .description {
|
||||
li a,
|
||||
li .user-count,
|
||||
li .description {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.extension-grid > li .card-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding-right: 0;
|
||||
li .user-count {
|
||||
opacity: 70%;
|
||||
font-size: small;
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
.store-stats {
|
||||
margin-top: auto;
|
||||
padding-top: 6px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
align-items: baseline;
|
||||
column-gap: 6px;
|
||||
row-gap: 4px;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
opacity: 0.72;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.store-stats-info {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.store-stats-sep {
|
||||
opacity: 0.45;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.store-links {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.store-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.store-link:hover {
|
||||
color: var(--vp-c-text-1);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.extension-grid > li .extension-name {
|
||||
li a {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
@@ -266,23 +171,33 @@ function formatStars(r: number): string {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.extension-grid > li .extension-name:hover {
|
||||
li a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.extension-grid > li .description {
|
||||
li div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
li .description {
|
||||
opacity: 90%;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0 !important;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
li .extension-name {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.pr {
|
||||
opacity: 70%;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { computed, toValue, MaybeRefOrGetter } from 'vue';
|
||||
|
||||
const MONTH_FORMATTER = new Intl.DateTimeFormat(
|
||||
globalThis?.navigator?.language,
|
||||
{
|
||||
month: 'long',
|
||||
},
|
||||
);
|
||||
|
||||
export default function (date: MaybeRefOrGetter<Date | string>) {
|
||||
return computed(() => {
|
||||
const d = new Date(toValue(date));
|
||||
return `${MONTH_FORMATTER.format(d)} ${d.getDate()}, ${d.getFullYear()}`;
|
||||
});
|
||||
}
|
||||
@@ -1,78 +1,55 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
export interface Extension {
|
||||
export interface ChromeExtension {
|
||||
id: string;
|
||||
name: string;
|
||||
iconUrl: string;
|
||||
weeklyActiveUsers: number;
|
||||
shortDescription: string;
|
||||
storeUrl: string;
|
||||
rating: number | undefined;
|
||||
users: number;
|
||||
}
|
||||
|
||||
export interface ExtensionResults {
|
||||
chrome: Extension[];
|
||||
firefox: Extension[];
|
||||
}
|
||||
|
||||
const operationName = 'WxtDocsUsedBy';
|
||||
const query = `query ${operationName}($chromeIds: [String!]!, $firefoxIds: [String!]!) {
|
||||
chromeExtensions(ids: $chromeIds) {
|
||||
const query = `query ${operationName}($ids:[String!]!) {
|
||||
chromeExtensions(ids: $ids) {
|
||||
id
|
||||
...ExtensionData
|
||||
name
|
||||
iconUrl
|
||||
weeklyActiveUsers
|
||||
shortDescription
|
||||
storeUrl
|
||||
rating
|
||||
}
|
||||
firefoxAddons(ids: $firefoxIds) {
|
||||
id: slug
|
||||
...ExtensionData
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExtensionData on Extension {
|
||||
name
|
||||
iconUrl
|
||||
shortDescription
|
||||
storeUrl
|
||||
rating
|
||||
users
|
||||
}`;
|
||||
|
||||
export default function (chromeIds: string[], firefoxSlugs: string[]) {
|
||||
const data = ref<ExtensionResults>();
|
||||
export default function (ids: string[]) {
|
||||
const data = ref<ChromeExtension[]>();
|
||||
const err = ref<unknown>();
|
||||
const isLoading = ref(true);
|
||||
|
||||
if (chromeIds.length === 0 && firefoxSlugs.length === 0) {
|
||||
data.value = { chrome: [], firefox: [] };
|
||||
isLoading.value = false;
|
||||
return { data, err, isLoading };
|
||||
}
|
||||
|
||||
fetch('https://queue.wxt.dev/api', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
operationName,
|
||||
query,
|
||||
variables: { chromeIds, firefoxIds: firefoxSlugs },
|
||||
variables: { ids },
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(async (res) => {
|
||||
isLoading.value = false;
|
||||
const { data: responseData } = await res.json();
|
||||
data.value = {
|
||||
chrome: responseData.chromeExtensions ?? [],
|
||||
firefox: responseData.firefoxAddons ?? [],
|
||||
};
|
||||
const {
|
||||
data: { chromeExtensions },
|
||||
} = await res.json();
|
||||
data.value = chromeExtensions;
|
||||
err.value = undefined;
|
||||
})
|
||||
.catch((error) => {
|
||||
isLoading.value = false;
|
||||
console.error(error);
|
||||
data.value = undefined;
|
||||
err.value = error;
|
||||
});
|
||||
|
||||
return { data, err, isLoading };
|
||||
return {
|
||||
data,
|
||||
err,
|
||||
};
|
||||
}
|
||||
|
||||
+106
-237
@@ -1,281 +1,150 @@
|
||||
import { Feed } from 'feed';
|
||||
// @ts-expect-error; It isn't TypeScript lib
|
||||
import footnote from 'markdown-it-footnote';
|
||||
import fs, { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { DefaultTheme, defineConfig } from 'vitepress';
|
||||
import addKnowledge from 'vitepress-knowledge';
|
||||
import {
|
||||
groupIconMdPlugin,
|
||||
groupIconVitePlugin,
|
||||
localIconLoader,
|
||||
} from 'vitepress-plugin-group-icons';
|
||||
import llmstxt from 'vitepress-plugin-llms';
|
||||
import { version as analyticsVersion } from '../../packages/analytics/package.json';
|
||||
import { version as autoIconsVersion } from '../../packages/auto-icons/package.json';
|
||||
import { version as i18nVersion } from '../../packages/i18n/package.json';
|
||||
import { version as runnerVersion } from '../../packages/runner/package.json';
|
||||
import { version as storageVersion } from '../../packages/storage/package.json';
|
||||
import { version as unocssVersion } from '../../packages/unocss/package.json';
|
||||
import { version as wxtVersion } from '../../packages/wxt/package.json';
|
||||
import { version as isBackgroundVersion } from '../../packages/is-background/package.json';
|
||||
import typedocSidebar from '../api/reference/typedoc-sidebar.json';
|
||||
import { meta, script } from './utils/head';
|
||||
import {
|
||||
menuGroup,
|
||||
menuItem,
|
||||
menuRoot,
|
||||
navItem,
|
||||
prepareTypedocSidebar,
|
||||
} from './utils/menus';
|
||||
import { generateCliDocs } from './plugins/generate-cli-docs';
|
||||
import typedocSidebar from '../api/typedoc-sidebar.json';
|
||||
|
||||
const origin = 'https://wxt.dev';
|
||||
const filteredTypedocSidebar = typedocSidebar.filter(
|
||||
(item) => item.text !== 'API',
|
||||
);
|
||||
// Typedoc's markdown theme adds collapse: true to all our items, event ones without any children,
|
||||
// so they need to be removed.
|
||||
function removeCollapsedWithNoItems(items: DefaultTheme.SidebarItem[]) {
|
||||
for (const item of items) {
|
||||
if (item.items) removeCollapsedWithNoItems(item.items);
|
||||
else delete item.collapsed;
|
||||
}
|
||||
}
|
||||
removeCollapsedWithNoItems(filteredTypedocSidebar);
|
||||
|
||||
const title = 'Next-gen Web Extension Framework';
|
||||
const titleSuffix = ' – WXT';
|
||||
|
||||
const description =
|
||||
"WXT provides the best developer experience, making it quick, easy, and fun to develop web extensions. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
|
||||
"WXT provides the best developer experience, making it quick, easy, and fun to develop chrome extensions for all browsers. With built-in utilties for building, zipping, and publishing your extension, it's easy to get started.";
|
||||
const ogTitle = `${title}${titleSuffix}`;
|
||||
const ogUrl = origin;
|
||||
const ogImage = `${origin}/social-preview.png`;
|
||||
|
||||
const otherPackages = {
|
||||
analytics: analyticsVersion,
|
||||
'auto-icons': autoIconsVersion,
|
||||
i18n: i18nVersion,
|
||||
storage: storageVersion,
|
||||
unocss: unocssVersion,
|
||||
runner: runnerVersion,
|
||||
'is-background': isBackgroundVersion,
|
||||
};
|
||||
|
||||
const knowledge = addKnowledge<DefaultTheme.Config>({
|
||||
serverUrl: 'https://knowledge.wxt.dev',
|
||||
paths: {
|
||||
'/': 'docs',
|
||||
'/api/': 'api-reference',
|
||||
'/blog/': 'blog',
|
||||
},
|
||||
layoutSelectors: {
|
||||
blog: '.container-content',
|
||||
},
|
||||
pageSelectors: {
|
||||
'examples.md': '#VPContent > .VPPage',
|
||||
'blog.md': '#VPContent > .VPPage',
|
||||
},
|
||||
});
|
||||
const ogUrl = 'https://wxt.dev';
|
||||
const ogImage = 'https://wxt.dev/social-preview.png';
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
extends: knowledge,
|
||||
|
||||
titleTemplate: `:title${titleSuffix}`,
|
||||
title: 'WXT',
|
||||
description,
|
||||
vite: {
|
||||
clearScreen: false,
|
||||
//TODO: REMOVE THIS @TS-EXPECT-ERROR AFTER BUMP VITEPRESS TO V2.0
|
||||
plugins: [
|
||||
// @ts-expect-error: Vite version mismatch between this project and the plugin
|
||||
llmstxt(),
|
||||
// @ts-expect-error: Vite version mismatch between this project and the plugin
|
||||
groupIconVitePlugin({
|
||||
customIcon: {
|
||||
'wxt.config.ts': localIconLoader(
|
||||
import.meta.url,
|
||||
'../public/logo.svg',
|
||||
),
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'yaml-loader',
|
||||
load: {
|
||||
handler: async (id) => {
|
||||
if (id.endsWith('.yml') || id.endsWith('.yaml')) {
|
||||
const obj = Bun.YAML.parse(await readFile(id, 'utf8'));
|
||||
return `export default ${JSON.stringify(obj)}`;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
plugins: [generateCliDocs()],
|
||||
},
|
||||
lastUpdated: true,
|
||||
sitemap: {
|
||||
hostname: origin,
|
||||
},
|
||||
|
||||
async buildEnd(site) {
|
||||
// @ts-expect-error: knowledge.buildEnd is not typed, but it exists.
|
||||
await knowledge.buildEnd(site);
|
||||
|
||||
// Only construct the RSS document for production builds
|
||||
const { default: blogDataLoader } = await import('./loaders/blog.data');
|
||||
const posts = await blogDataLoader.load();
|
||||
const feed = new Feed({
|
||||
copyright: 'MIT',
|
||||
id: 'wxt',
|
||||
title: 'WXT Blog',
|
||||
link: `${origin}/blog`,
|
||||
});
|
||||
posts.forEach((post) => {
|
||||
feed.addItem({
|
||||
date: post.frontmatter.date,
|
||||
link: new URL(post.url, origin).href,
|
||||
title: post.frontmatter.title,
|
||||
description: post.frontmatter.description,
|
||||
});
|
||||
});
|
||||
// console.log('rss.xml:');
|
||||
// console.log(feed.rss2());
|
||||
await fs.writeFile(join(site.outDir, 'rss.xml'), feed.rss2());
|
||||
hostname: 'https://wxt.dev',
|
||||
},
|
||||
|
||||
head: [
|
||||
meta('og:type', 'website'),
|
||||
meta('og:title', ogTitle),
|
||||
meta('og:image', ogImage),
|
||||
meta('og:url', ogUrl),
|
||||
meta('og:description', description),
|
||||
meta('twitter:card', 'summary_large_image', { useName: true }),
|
||||
script('https://umami.aklinker1.io/script.js', {
|
||||
'data-website-id': 'c1840c18-a12c-4a45-a848-55ae85ef7915',
|
||||
async: '',
|
||||
}),
|
||||
['meta', { property: 'og:type', content: 'website' }],
|
||||
['meta', { property: 'og:title', content: ogTitle }],
|
||||
['meta', { property: 'og:image', content: ogImage }],
|
||||
['meta', { property: 'og:url', content: ogUrl }],
|
||||
['meta', { property: 'og:description', content: description }],
|
||||
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
async: '',
|
||||
'data-website-id': 'c1840c18-a12c-4a45-a848-55ae85ef7915',
|
||||
src: 'https://umami.aklinker1.io/script.js',
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
markdown: {
|
||||
config: (md) => {
|
||||
md.use(footnote);
|
||||
md.use(groupIconMdPlugin);
|
||||
},
|
||||
languageAlias: {
|
||||
mjs: 'js',
|
||||
},
|
||||
},
|
||||
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
logo: {
|
||||
src: '/logo.svg',
|
||||
alt: 'WXT logo',
|
||||
},
|
||||
|
||||
footer: {
|
||||
message: [
|
||||
'<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:
|
||||
'Copyright © 2023-present <a href="https://github.com/aklinker1">Aaron Klinker</a>',
|
||||
},
|
||||
|
||||
editLink: {
|
||||
pattern: 'https://github.com/wxt-dev/wxt/edit/main/docs/:path',
|
||||
},
|
||||
|
||||
search: {
|
||||
provider: 'local',
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'discord', link: 'https://discord.gg/ZFsZqGery9' },
|
||||
{ icon: 'github', link: 'https://github.com/wxt-dev/wxt' },
|
||||
],
|
||||
|
||||
nav: [
|
||||
navItem('Guide', '/guide/installation'),
|
||||
navItem('Examples', '/examples'),
|
||||
navItem('API', '/api/reference/wxt'),
|
||||
navItem('Blog', '/blog'),
|
||||
navItem(`v${wxtVersion}`, [
|
||||
navItem('wxt', [
|
||||
navItem(`v${wxtVersion}`, '/'),
|
||||
navItem(
|
||||
`Changelog`,
|
||||
'https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md',
|
||||
),
|
||||
]),
|
||||
navItem(
|
||||
'Other Packages',
|
||||
Object.entries(otherPackages).map(([name, version]) =>
|
||||
navItem(`@wxt-dev/${name} — ${version}`, `/${name}`),
|
||||
),
|
||||
),
|
||||
]),
|
||||
{ text: 'Guide', link: '/guide/installation.md' },
|
||||
{ text: 'Entrypoints', link: '/entrypoints/background.md' },
|
||||
{ text: 'Examples', link: '/examples.md' },
|
||||
{ text: 'API', link: '/api/cli.md' },
|
||||
],
|
||||
|
||||
sidebar: {
|
||||
'/guide/': menuRoot([
|
||||
menuGroup('Get Started', '/guide/', [
|
||||
menuItem('Introduction', 'introduction.md'),
|
||||
menuItem('Installation', 'installation.md'),
|
||||
]),
|
||||
menuGroup('Essentials', '/guide/essentials/', [
|
||||
menuItem('Project Structure', 'project-structure.md'),
|
||||
menuItem('Entrypoints', 'entrypoints.md'),
|
||||
menuGroup(
|
||||
'Configuration',
|
||||
'/guide/essentials/config/',
|
||||
[
|
||||
menuItem('Manifest', 'manifest.md'),
|
||||
menuItem('Browser Startup', 'browser-startup.md'),
|
||||
menuItem('Auto-imports', 'auto-imports.md'),
|
||||
menuItem('Environment Variables', 'environment-variables.md'),
|
||||
menuItem('Runtime Config', 'runtime.md'),
|
||||
menuItem('Vite', 'vite.md'),
|
||||
menuItem('Build Mode', 'build-mode.md'),
|
||||
menuItem('TypeScript', 'typescript.md'),
|
||||
menuItem('Hooks', 'hooks.md'),
|
||||
menuItem('Entrypoint Loaders', 'entrypoint-loaders.md'),
|
||||
],
|
||||
true,
|
||||
),
|
||||
menuItem('Extension APIs', 'extension-apis.md'),
|
||||
menuItem('Assets', 'assets.md'),
|
||||
menuItem('Target Different Browsers', 'target-different-browsers.md'),
|
||||
menuItem('Content Scripts', 'content-scripts.md'),
|
||||
menuItem('Storage', 'storage.md'),
|
||||
menuItem('Messaging', 'messaging.md'),
|
||||
menuItem('I18n', 'i18n.md'),
|
||||
menuItem('Favicons', 'favicons.md'),
|
||||
menuItem('Scripting', 'scripting.md'),
|
||||
menuItem('WXT Modules', 'wxt-modules.md'),
|
||||
menuItem('Frontend Frameworks', 'frontend-frameworks.md'),
|
||||
menuItem('ES Modules', 'es-modules.md'),
|
||||
menuItem('Remote Code', 'remote-code.md'),
|
||||
menuItem('Unit Testing', 'unit-testing.md'),
|
||||
menuItem('E2E Testing', 'e2e-testing.md'),
|
||||
menuItem('Publishing', 'publishing.md'),
|
||||
menuItem('Testing Updates', 'testing-updates.md'),
|
||||
]),
|
||||
menuGroup('Resources', '/guide/resources/', [
|
||||
menuItem('Compare', 'compare.md'),
|
||||
menuItem('FAQ', 'faq.md'),
|
||||
menuItem('Community', 'community.md'),
|
||||
menuItem('Upgrading WXT', 'upgrading.md'),
|
||||
menuItem('Migrate to WXT', 'migrate.md'),
|
||||
menuItem('How WXT Works', 'how-wxt-works.md'),
|
||||
]),
|
||||
]),
|
||||
'/api/': menuRoot([
|
||||
menuGroup(
|
||||
'CLI Reference',
|
||||
'/api/cli/',
|
||||
[
|
||||
menuItem('wxt', 'wxt.md'),
|
||||
menuItem('wxt build', 'wxt-build.md'),
|
||||
menuItem('wxt zip', 'wxt-zip.md'),
|
||||
menuItem('wxt prepare', 'wxt-prepare.md'),
|
||||
menuItem('wxt clean', 'wxt-clean.md'),
|
||||
menuItem('wxt init', 'wxt-init.md'),
|
||||
menuItem('wxt submit', 'wxt-submit.md'),
|
||||
menuItem('wxt submit init', 'wxt-submit-init.md'),
|
||||
'/guide/': [
|
||||
{
|
||||
text: 'Guide',
|
||||
items: [
|
||||
{ text: 'Introduction', link: '/guide/introduction.md' },
|
||||
{ text: 'Installation', link: '/guide/installation.md' },
|
||||
{ text: 'Configuration', link: '/guide/configuration.md' },
|
||||
{ text: 'Entrypoints', link: '/guide/entrypoints.md' },
|
||||
{ text: 'Assets', link: '/guide/assets.md' },
|
||||
{ text: 'Multiple Browsers', link: '/guide/multiple-browsers.md' },
|
||||
{ text: 'Publishing', link: '/guide/publishing.md' },
|
||||
{ text: 'Auto-imports', link: '/guide/auto-imports.md' },
|
||||
{ text: 'Manifest.json', link: '/guide/manifest.md' },
|
||||
{ text: 'Extension APIs', link: '/guide/extension-apis.md' },
|
||||
{ text: 'Storage', link: '/guide/storage.md' },
|
||||
{ text: 'Content Script UI', link: '/guide/content-script-ui.md' },
|
||||
{ text: 'Remote Code', link: '/guide/remote-code.md' },
|
||||
{ text: 'Development', link: '/guide/development.md' },
|
||||
{ text: 'Testing', link: '/guide/testing.md' },
|
||||
{ text: 'Vite', link: '/guide/vite.md' },
|
||||
],
|
||||
true,
|
||||
),
|
||||
menuGroup('API Reference', prepareTypedocSidebar(typedocSidebar), true),
|
||||
]),
|
||||
},
|
||||
{
|
||||
text: 'Other',
|
||||
items: [
|
||||
{ text: 'Migrate to WXT', link: '/guide/migrate-to-wxt.md' },
|
||||
{ text: 'Compare', link: '/guide/compare.md' },
|
||||
],
|
||||
},
|
||||
],
|
||||
'/entrypoints/': [
|
||||
{
|
||||
text: 'Entrypoints',
|
||||
items: [
|
||||
{ text: 'Background', link: '/entrypoints/background.md' },
|
||||
{ text: 'Bookmarks', link: '/entrypoints/bookmarks.md' },
|
||||
{
|
||||
text: 'Content Scripts',
|
||||
link: '/entrypoints/content-scripts.md',
|
||||
},
|
||||
{ text: 'CSS', link: '/entrypoints/css.md' },
|
||||
{ text: 'Devtools', link: '/entrypoints/devtools.md' },
|
||||
{ text: 'History', link: '/entrypoints/history.md' },
|
||||
{ text: 'Newtab', link: '/entrypoints/newtab.md' },
|
||||
{ text: 'Options', link: '/entrypoints/options.md' },
|
||||
{ text: 'Popup', link: '/entrypoints/popup.md' },
|
||||
{ text: 'Sandbox', link: '/entrypoints/sandbox.md' },
|
||||
{ text: 'Sidepanel', link: '/entrypoints/sidepanel.md' },
|
||||
{ text: 'Unlisted Pages', link: '/entrypoints/unlisted-pages.md' },
|
||||
{
|
||||
text: 'Unlisted Scripts',
|
||||
link: '/entrypoints/unlisted-scripts.md',
|
||||
},
|
||||
].sort((l, r) => l.text.localeCompare(r.text)),
|
||||
},
|
||||
],
|
||||
'/api/': [
|
||||
{
|
||||
items: [
|
||||
{ text: 'CLI', link: '/api/cli.md' },
|
||||
{
|
||||
text: 'Modules',
|
||||
items: filteredTypedocSidebar,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
socialLinks: [{ icon: 'github', link: 'https://github.com/wxt-dev/wxt' }],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { type ContentData, createContentLoader } from 'vitepress';
|
||||
import { PostFrontmatter } from '../utils/types';
|
||||
|
||||
declare const data: Array<ContentData & { frontmatter: PostFrontmatter }>;
|
||||
export { data };
|
||||
|
||||
export default createContentLoader<
|
||||
Array<ContentData & { frontmatter: PostFrontmatter }>
|
||||
>('blog/*.md');
|
||||
@@ -1,58 +0,0 @@
|
||||
import consola from 'consola';
|
||||
import spawn from 'nano-spawn';
|
||||
import { resolve } from 'node:path';
|
||||
import { version } from '../../../packages/wxt/package.json';
|
||||
|
||||
const cliDir = resolve('packages/wxt/src/cli/commands');
|
||||
const cliDirGlob = resolve(cliDir, '**');
|
||||
|
||||
export default {
|
||||
watch: [cliDirGlob],
|
||||
async load() {
|
||||
consola.info(`Generating CLI docs`);
|
||||
|
||||
const [wxt, build, zip, prepare, clean, init, submit, submitInit] =
|
||||
await Promise.all([
|
||||
getWxtHelp(''),
|
||||
getWxtHelp('build'),
|
||||
getWxtHelp('zip'),
|
||||
getWxtHelp('prepare'),
|
||||
getWxtHelp('clean'),
|
||||
getWxtHelp('init'),
|
||||
getPublishExtensionHelp(''),
|
||||
getPublishExtensionHelp('init'),
|
||||
]);
|
||||
|
||||
consola.success(`Generated CLI docs`);
|
||||
return {
|
||||
wxt,
|
||||
build,
|
||||
zip,
|
||||
prepare,
|
||||
clean,
|
||||
init,
|
||||
submit,
|
||||
submitInit,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
async function getHelp(command: string): Promise<string> {
|
||||
const args = command.split(' ');
|
||||
const result = await spawn(args[0], [...args.slice(1), '--help'], {
|
||||
cwd: 'packages/wxt',
|
||||
});
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
async function getWxtHelp(command: string): Promise<string> {
|
||||
const res = await getHelp(`bun run --silent wxt ${command}`.trim());
|
||||
return res.replaceAll('{{version}}', version);
|
||||
}
|
||||
|
||||
async function getPublishExtensionHelp(command: string): Promise<string> {
|
||||
const res = await getHelp(
|
||||
`bun run --silent publish-extension ${command}`.trim(),
|
||||
);
|
||||
return res.replace(/\$ publish-extension/g, '$ wxt submit');
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# CLI Reference
|
||||
|
||||
> Reference generated from `wxt <command> --help`
|
||||
|
||||
{{ DOCS }}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { relative, resolve } from 'node:path';
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { format } from 'prettier';
|
||||
import consola from 'consola';
|
||||
import { execaCommand } from 'execa';
|
||||
|
||||
let hasGenerated = false;
|
||||
|
||||
const cliDir = resolve('src/cli/commands');
|
||||
const cliDirGlob = resolve(cliDir, '**');
|
||||
const cliTemplatePath = resolve('docs/.vitepress/plugins/cli.tpl.md');
|
||||
const cliPath = resolve('docs/api/cli.md');
|
||||
|
||||
const PREFACE = `<!--
|
||||
DO NOT EDIT
|
||||
Generated by \`wxt/docs/.vitepress/plugins/generate-config-docs.ts\`
|
||||
To make changes to the config reference, update the JSDoc in \`src/core/types/external.ts\`.
|
||||
-->`;
|
||||
|
||||
export function generateCliDocs() {
|
||||
writeFileSync(cliPath, '');
|
||||
|
||||
const generateDocs = async () => {
|
||||
consola.info(`Generating ${relative(process.cwd(), cliPath)}`);
|
||||
try {
|
||||
const res = await execaCommand('pnpm -s wxt --help');
|
||||
const dev = splitInfo(res.stdout);
|
||||
const lines: Array<string | string[]> = [
|
||||
`## \`wxt\``,
|
||||
'```sh',
|
||||
dev.rest,
|
||||
'```',
|
||||
];
|
||||
|
||||
const commands = await Promise.all(
|
||||
extractCommands(dev.info).map(async (command) => {
|
||||
const res = await execaCommand(`pnpm -s wxt ${command} --help`);
|
||||
const { rest: docs } = splitInfo(res.stdout);
|
||||
return [`## \`wxt ${command}\``, '```sh', docs, '```'];
|
||||
}),
|
||||
);
|
||||
lines.push(...commands);
|
||||
|
||||
const text = await format(
|
||||
PREFACE +
|
||||
'\n\n' +
|
||||
readFileSync(cliTemplatePath, 'utf-8').replace(
|
||||
'{{ DOCS }}',
|
||||
lines.flat().join('\n'),
|
||||
),
|
||||
{ parser: 'markdown' },
|
||||
);
|
||||
|
||||
writeFileSync(cliPath, text);
|
||||
consola.success(`Generated ${relative(process.cwd(), cliPath)}`);
|
||||
} catch (err) {
|
||||
consola.fail(`Failed to generate ${relative(process.cwd(), cliPath)}`);
|
||||
consola.error(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'docs:generate-cli-docs',
|
||||
async config() {
|
||||
if (!hasGenerated) {
|
||||
hasGenerated = true;
|
||||
await generateDocs();
|
||||
}
|
||||
},
|
||||
configureServer(server: any) {
|
||||
server.watcher.add(cliDirGlob);
|
||||
},
|
||||
async handleHotUpdate(ctx: { file: string }) {
|
||||
if (ctx.file === cliTemplatePath || ctx.file.includes(cliDir)) {
|
||||
await generateDocs();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function splitInfo(text: string): { info: string; rest: string } {
|
||||
const infoStart = text.indexOf('For more info,');
|
||||
const infoEnd = text.indexOf('\n\nOptions:');
|
||||
|
||||
if (infoStart === -1 || infoEnd === -1) {
|
||||
return { info: '', rest: text };
|
||||
}
|
||||
|
||||
const info = text.substring(infoStart, infoEnd).trim();
|
||||
const rest = text.replace(info, '').trim().replace('\n\n\n\n', '\n\n');
|
||||
|
||||
return { info, rest };
|
||||
}
|
||||
|
||||
function extractCommands(info: string): string[] {
|
||||
const commands: string[] = [];
|
||||
|
||||
// Split the info by line and iterate through each line
|
||||
info.split('\n').forEach((line) => {
|
||||
// Use regex to capture the command after "$ wxt " and before "--help"
|
||||
const match = line.match(/\$ wxt (\w+) --help/);
|
||||
if (match && match[1]) {
|
||||
commands.push(match[1]);
|
||||
}
|
||||
});
|
||||
|
||||
return commands;
|
||||
}
|
||||
@@ -1,104 +1,77 @@
|
||||
/* 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);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--wxt-c-green: #53bc4a;
|
||||
--wxt-c-green-1: #67d45e;
|
||||
--wxt-c-green-2: #329929;
|
||||
--wxt-c-green-3: #21651b;
|
||||
--wxt-c-green-soft: rgba(103, 212, 94, 0.14);
|
||||
--wxt-c-green-2: #4fa048;
|
||||
--wxt-c-green-3: #447e3f;
|
||||
}
|
||||
|
||||
/* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */
|
||||
|
||||
:root {
|
||||
--vp-c-brand: var(--wxt-c-green);
|
||||
--vp-c-brand-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);
|
||||
|
||||
--vp-button-brand-bg: var(--wxt-c-green);
|
||||
--vp-button-brand-hover-bg: var(--wxt-c-green-2);
|
||||
--vp-button-brand-active-bg: var(--wxt-c-green-3);
|
||||
|
||||
--vp-code-link-color: var(--wxt-c-green);
|
||||
|
||||
/* --vp-c-text-1: var(--wxt-c-green-1); */
|
||||
|
||||
--vp-button-brand-text: var(--vp-c-black);
|
||||
--vp-button-brand-hover-text: var(--vp-c-black);
|
||||
--vp-button-brand-active-text: var(--vp-c-black);
|
||||
|
||||
--vp-custom-block-tip-border: var(--wxt-c-green);
|
||||
--vp-custom-block-tip-text: var(--wxt-c-green);
|
||||
|
||||
/* --vp-code-block-bg: #222422;
|
||||
--vp-code-copy-code-bg: #313431;
|
||||
--vp-code-copy-code-hover-bg: #3c403c; */
|
||||
|
||||
--vp-custom-block-tip-bg: var(--vp-code-block-bg);
|
||||
--vp-custom-block-info-bg: var(--vp-code-block-bg);
|
||||
|
||||
--vp-code-color: #476582;
|
||||
}
|
||||
|
||||
/* Customize Individual Components */
|
||||
.vp-doc a {
|
||||
color: var(--wxt-c-green);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-c-bg: #131413;
|
||||
|
||||
--vp-c-bg-soft: #1a1b1a;
|
||||
--vp-c-bg-soft-up: #1f201f;
|
||||
--vp-c-bg-soft-down: #262926;
|
||||
--vp-c-bg-soft-mute: #242424;
|
||||
|
||||
--vp-c-bg-alt: #171817;
|
||||
|
||||
--vp-c-default: #313136;
|
||||
--vp-c-default-1: #3a3a3c;
|
||||
--vp-c-default-2: #505053;
|
||||
--vp-c-default-3: #2c2c30;
|
||||
--vp-c-default-soft: #252529;
|
||||
|
||||
--vp-code-block-bg: #191a19;
|
||||
--vp-code-copy-code-bg: #212321;
|
||||
--vp-code-copy-code-hover-bg: #292d29;
|
||||
|
||||
--vp-custom-block-info-bg: #191a19;
|
||||
|
||||
--vp-code-color: #c9def1;
|
||||
}
|
||||
|
||||
.vp-doc .no-vertical-dividers th,
|
||||
.vp-doc .no-vertical-dividers td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.vp-doc .no-vertical-dividers 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);
|
||||
}
|
||||
|
||||
.netlify-badge {
|
||||
display: inline-flex;
|
||||
line-height: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.netlify-badge img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.light-netlify {
|
||||
display: inline-flex;
|
||||
}
|
||||
.dark .light-netlify {
|
||||
display: none;
|
||||
}
|
||||
.dark-netlify {
|
||||
display: none;
|
||||
}
|
||||
.dark .dark-netlify {
|
||||
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;
|
||||
width: 100%;
|
||||
height: 110vh;
|
||||
background-size: cover;
|
||||
background-position: 90%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.dark .VPHome::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 10vh;
|
||||
top: 100vh;
|
||||
background: linear-gradient(to bottom, transparent, var(--vp-c-bg));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.item {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -2,20 +2,13 @@ import DefaultTheme from 'vitepress/theme';
|
||||
import Icon from '../components/Icon.vue';
|
||||
import EntrypointPatterns from '../components/EntrypointPatterns.vue';
|
||||
import UsingWxtSection from '../components/UsingWxtSection.vue';
|
||||
import ExampleSearch from '../components/ExampleSearch.vue';
|
||||
import BlogLayout from '../components/BlogLayout.vue';
|
||||
import './custom.css';
|
||||
import 'virtual:group-icons.css';
|
||||
import type { EnhanceAppContext } from 'vitepress/client';
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
enhanceApp(ctx: EnhanceAppContext) {
|
||||
ctx.app
|
||||
.component('Icon', Icon)
|
||||
.component('EntrypointPatterns', EntrypointPatterns)
|
||||
.component('UsingWxtSection', UsingWxtSection)
|
||||
.component('ExampleSearch', ExampleSearch)
|
||||
.component('blog', BlogLayout);
|
||||
enhanceApp(ctx) {
|
||||
ctx.app.component('Icon', Icon);
|
||||
ctx.app.component('EntrypointPatterns', EntrypointPatterns);
|
||||
ctx.app.component('UsingWxtSection', UsingWxtSection);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { HeadConfig } from 'vitepress';
|
||||
|
||||
export function meta(
|
||||
property: string,
|
||||
content: string,
|
||||
options?: { useName: boolean },
|
||||
): HeadConfig {
|
||||
return [
|
||||
'meta',
|
||||
{
|
||||
[options?.useName ? 'name' : 'property']: property,
|
||||
content,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function script(
|
||||
src: string,
|
||||
props: Record<string, string> = {},
|
||||
): HeadConfig {
|
||||
return [
|
||||
'script',
|
||||
{
|
||||
...props,
|
||||
src,
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import { DefaultTheme } from 'vitepress';
|
||||
|
||||
type SidebarItem = DefaultTheme.SidebarItem;
|
||||
type NavItemWithChildren = DefaultTheme.NavItemWithChildren;
|
||||
type NavItemChildren = DefaultTheme.NavItemChildren;
|
||||
|
||||
export function navItem(text: string): NavItemChildren;
|
||||
export function navItem(text: string, link: string): NavItemChildren;
|
||||
export function navItem(text: string, items: any[]): NavItemWithChildren;
|
||||
export function navItem(text: string, arg2?: unknown): any {
|
||||
if (typeof arg2 === 'string') {
|
||||
return { text, link: arg2 };
|
||||
} else if (Array.isArray(arg2)) {
|
||||
return { text, items: arg2 };
|
||||
}
|
||||
return { text };
|
||||
}
|
||||
|
||||
export function menuRoot(items: SidebarItem[]) {
|
||||
return items.map((item) => {
|
||||
// item.collapsed = false; // uncomment to expand all level-0 items
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
export function menuGroup(
|
||||
text: string,
|
||||
items: SidebarItem[],
|
||||
collapsible?: boolean,
|
||||
): SidebarItem;
|
||||
export function menuGroup(
|
||||
text: string,
|
||||
base: string,
|
||||
items: SidebarItem[],
|
||||
collapsible?: boolean,
|
||||
): SidebarItem;
|
||||
export function menuGroup(
|
||||
text: string,
|
||||
a: string | SidebarItem[],
|
||||
b?: SidebarItem[] | boolean,
|
||||
c?: boolean,
|
||||
): SidebarItem {
|
||||
if (typeof a === 'string' && Array.isArray(b)) {
|
||||
return {
|
||||
text,
|
||||
base: a,
|
||||
items: b,
|
||||
collapsed: c,
|
||||
};
|
||||
}
|
||||
if (typeof a !== 'string' && !Array.isArray(b))
|
||||
return {
|
||||
text,
|
||||
items: a,
|
||||
collapsed: b,
|
||||
};
|
||||
|
||||
throw Error('Unknown overload');
|
||||
}
|
||||
|
||||
export function menuItem(
|
||||
text: string,
|
||||
link: string,
|
||||
items?: SidebarItem[],
|
||||
): SidebarItem {
|
||||
if (items) {
|
||||
return { text, link, items };
|
||||
}
|
||||
return { text, link };
|
||||
}
|
||||
|
||||
/** Clean up and add badges to typedoc leaf sections */
|
||||
export function prepareTypedocSidebar(items: SidebarItem[]) {
|
||||
// skip contents file
|
||||
const filtered = items.slice(1);
|
||||
|
||||
// remove Typedoc's collapse: true from text nodes
|
||||
const prepareItems = (items: DefaultTheme.SidebarItem[], depth = 0) => {
|
||||
for (const item of items) {
|
||||
if (item.items) {
|
||||
prepareItems(item.items, depth + 1);
|
||||
const hasLeaves = item.items.some((item) => !item.items);
|
||||
if (hasLeaves) {
|
||||
item.text += ` <span class="badge">${item.items.length}</span>`;
|
||||
}
|
||||
} else {
|
||||
delete item.collapsed;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
prepareItems(filtered);
|
||||
|
||||
return filtered;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { ContentData } from 'vitepress';
|
||||
|
||||
export interface Example {
|
||||
name: string;
|
||||
description?: string;
|
||||
url: string;
|
||||
searchText: string;
|
||||
apis: string[];
|
||||
permissions: string[];
|
||||
packages: string[];
|
||||
}
|
||||
|
||||
export type ExamplesMetadata = {
|
||||
examples: Example[];
|
||||
allApis: string[];
|
||||
allPermissions: string[];
|
||||
allPackages: string[];
|
||||
};
|
||||
|
||||
export type KeySelectedObject = Record<string, boolean | undefined>;
|
||||
|
||||
export interface PostFrontmatter {
|
||||
title: string;
|
||||
description?: string;
|
||||
date: Date;
|
||||
authors: { name: string; github: string }[];
|
||||
}
|
||||
|
||||
export interface Post
|
||||
extends Omit<ContentData, 'frontmatter'>, Omit<PostFrontmatter, 'date'> {
|
||||
date: Date;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<!--@include: ../packages/analytics/README.md-->
|
||||
@@ -1,7 +0,0 @@
|
||||
# `wxt build`
|
||||
|
||||
<script setup>
|
||||
import { data } from '../../.vitepress/loaders/cli.data.ts'
|
||||
</script>
|
||||
|
||||
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.build }}</code></pre></div>
|
||||
@@ -1,7 +0,0 @@
|
||||
# `wxt clean`
|
||||
|
||||
<script setup>
|
||||
import { data } from '../../.vitepress/loaders/cli.data.ts'
|
||||
</script>
|
||||
|
||||
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.clean }}</code></pre></div>
|
||||
@@ -1,7 +0,0 @@
|
||||
# `wxt init`
|
||||
|
||||
<script setup>
|
||||
import { data } from '../../.vitepress/loaders/cli.data.ts'
|
||||
</script>
|
||||
|
||||
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.init }}</code></pre></div>
|
||||
@@ -1,7 +0,0 @@
|
||||
# `wxt prepare`
|
||||
|
||||
<script setup>
|
||||
import { data } from '../../.vitepress/loaders/cli.data.ts'
|
||||
</script>
|
||||
|
||||
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.prepare }}</code></pre></div>
|
||||
@@ -1,9 +0,0 @@
|
||||
# `wxt submit init`
|
||||
|
||||
> Alias for [`publish-browser-extension`](https://www.npmjs.com/package/publish-browser-extension)
|
||||
|
||||
<script setup>
|
||||
import { data } from '../../.vitepress/loaders/cli.data.ts'
|
||||
</script>
|
||||
|
||||
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.submitInit }}</code></pre></div>
|
||||
@@ -1,9 +0,0 @@
|
||||
# `wxt submit`
|
||||
|
||||
> Alias for [`publish-browser-extension`](https://www.npmjs.com/package/publish-browser-extension)
|
||||
|
||||
<script setup>
|
||||
import { data } from '../../.vitepress/loaders/cli.data.ts'
|
||||
</script>
|
||||
|
||||
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.submit }}</code></pre></div>
|
||||
@@ -1,7 +0,0 @@
|
||||
# `wxt zip`
|
||||
|
||||
<script setup>
|
||||
import { data } from '../../.vitepress/loaders/cli.data.ts'
|
||||
</script>
|
||||
|
||||
<div class="language-sh vp-adaptive-theme active"><pre class="shiki shiki-themes github-light github-dark vp-code"><code>{{ data.zip }}</code></pre></div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user