Compare commits

...

110 Commits

Author SHA1 Message Date
Aaron 229dfc822d 0.19.0-alpha1 2024-07-24 15:47:44 -05:00
Aaron 7177405c11 Fix dead links 2024-07-24 15:36:40 -05:00
Aaron 24e4abc087 Fix docs 2024-07-24 15:27:04 -05:00
Aaron ac9a4380b1 cleanup 2024-07-24 15:14:25 -05:00
Aaron 71620a02ab Fix tests 2024-07-24 15:13:38 -05:00
Aaron 0aca961629 Fix browser types 2024-07-24 14:59:36 -05:00
Aaron d64af5d4f0 Move option out of experimental 2024-07-24 13:39:30 -05:00
Aaron 7eb0a36245 WIP 2024-07-24 13:31:09 -05:00
Aaron 14bae66bba Update testing setup 2024-07-24 12:10:59 -05:00
Aaron e4ec4a28f9 Exclude dev-only declaration files from build 2024-07-24 11:30:44 -05:00
Aaron 7b32dd7062 Restructure types 2024-07-24 11:28:35 -05:00
Aaron b1ef71a77b Cleanup 2024-07-24 11:23:44 -05:00
Aaron 2f7fb30467 Fix everything 2024-07-24 11:06:43 -05:00
Aaron 4c684e7966 WIP 2024-07-24 08:54:16 -05:00
Aaron 8d5fbb2a32 feat(experimental): Replace disableWebextensionPolyfill with extensionApi 2024-07-24 08:51:34 -05:00
Aaron 5eb5be7123 perf(size)!: Switch from tsup to unbuild for building WXT (#848) 2024-07-24 08:48:48 -05:00
Florian Metz 886b2081c2 fix: wrong module hook type (#849) 2024-07-23 20:02:26 -05:00
Radek Grebski c69e51ce50 docs: Added "RespondEasy" to homepage (#844)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-23 09:14:59 -05:00
Hagatopaxi 861efa3415 docs: fix typo (#845) 2024-07-22 23:53:00 -05:00
Aaron 792b57542d updtae lockfile 2024-07-21 11:53:34 -05:00
Aaron 97e6678e5b Upgrade vitest's listed version 2024-07-21 11:46:23 -05:00
Aaron 66c4ec3f99 chore(dev-deps): Upgrade happy-dom from 13 to 14 (#843) 2024-07-21 11:44:52 -05:00
Aaron 8988d6a241 chore(deps): Upgrade minimatch from 9 to 10 (#840) 2024-07-21 11:42:33 -05:00
Aaron b52856bea4 chore(deps): Upgrade webextension-polyfill from 0.10 to 0.12 (#842) 2024-07-21 11:39:40 -05:00
Aaron 16fee19b85 chore(deps): Upgrade ora from 7 to 8 (#841) 2024-07-21 11:39:03 -05:00
Aaron 14011c19cb chore(deps): Upgrade vite-node from 1.6 to 2.0 (#839) 2024-07-21 11:36:02 -05:00
Aaron 005a9dff16 chore(deps): Upgrade esbuild from 0.19.12 to 0.23.0 (#838) 2024-07-21 11:33:39 -05:00
Aaron 3109bbadbe chore(deps): Upgrade async-mutex from 0.4.1 to 0.5.0 (#837) 2024-07-21 11:31:35 -05:00
Aaron 34ae689569 chore(dev-deps): Upgrade vitest from 1.6 to 2.0 (#836) 2024-07-21 11:24:23 -05:00
Aaron 1f216dd57b chore(deps): Bump all non-major dependencies (#834) 2024-07-21 10:01:51 -05:00
GitHub Actions a7963e23db chore(release): wxt v0.18.15 2024-07-21 13:43:02 +00:00
Aaron 069bd6c435 fix: Externalize app config during dependency optimization (#833) 2024-07-21 08:37:09 -05:00
Aaron 88a1244ca9 fix: Build latest version of package before packing 2024-07-21 08:36:45 -05:00
Aaron 7ee391f688 fix: Don't throw error if localization file is missing (#832) 2024-07-21 07:53:38 -05:00
Aaron 2b3eab9c2a docs: Add link to google's update testing tool in migration guide and improve host permission docs 2024-07-20 11:33:17 -05:00
eetann a3fc9e1e18 docs: fix link to unimport (#826) 2024-07-17 07:40:34 -05:00
Aaron 559d32e6eb Update README.md 2024-07-12 08:19:59 -05:00
Aaron f904b79367 Update README.md 2024-07-12 08:19:33 -05:00
eetann 9cd7cca81c docs: fix links to Guide pages (#821) 2024-07-12 07:25:11 -05:00
GitHub Actions 3366c1e35f chore(release): wxt v0.18.14 2024-07-09 16:03:18 +00:00
Aaron 82be31a408 fix(modules): Add types from all wxt node_modules, not just ones with config (#817) 2024-07-09 10:58:41 -05:00
GitHub Actions d767f3f598 chore(release): wxt v0.18.13 2024-07-09 15:07:38 +00:00
Ayub Kokabi 48967c7c5f feat: add XPath support to getAnchor() (#813)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-09 10:02:00 -05:00
Aaron 6c4da7225d fix: Add debug logs for vite builder (#816) 2024-07-09 09:58:59 -05:00
多吃点 e9025fb13e docs: Update manifest.md (#815) 2024-07-09 08:12:38 -05:00
jy00566722 8db6b3187b docs: Add "Exchange Rate Converter" extensions to homepage (#808) 2024-07-07 14:41:48 -05:00
Aaron a19f9e12c4 feat(config): dev.server.hostname (#807) 2024-07-07 09:31:38 -05:00
Farzad Soltani a82eb46511 docs: fix grammar (#806) 2024-07-07 09:16:03 -05:00
GitHub Actions e50f2b047a chore(release): wxt v0.18.12 2024-07-07 14:10:47 +00:00
Aaron 1b41cd26ee chore(deps): Upgrade web-ext-run (0.2.0 to 0.2.1) (#804) 2024-07-07 08:57:29 -05:00
Aaron e32fbed1b7 docs: Update README (#802) 2024-07-06 08:29:23 -05:00
sacrosanctic f8acdd6352 doc: detail issues with vite plugins (#799)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-06 08:01:22 -05:00
ntnyq 9ea89c6340 fix: Add missing name to ESLint v9 autoImports config (#801) 2024-07-06 08:01:10 -05:00
Margarita Balkheeva a521a80d43 docs: Add "MaxFocus" extension to homepage (#795)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-04 14:38:44 -05:00
sacrosanctic 4c6a57dd33 docs: rephrase a paragraph (#797) 2024-07-03 16:16:41 -05:00
Florian Metz 3ce8c8b1f4 perf: create zip using streams (#793) 2024-07-03 01:37:58 -05:00
deyu 4ee8f7837e docs: Add "Vanilla Pudding" extension to homepage (#794) 2024-07-03 01:36:32 -05:00
Aaron 1e8e5488af feat: Support runtime config in app.config.ts (#792) 2024-07-02 17:19:54 -05:00
GitHub Actions 5704765cc4 chore(release): wxt v0.18.11 2024-07-02 16:40:05 +00:00
GitHub Actions 8c7adbf463 chore(release): @wxt-dev/module-react v1.1.0 2024-07-02 16:34:30 +00:00
Aaron 8c4436efe8 Update solid template to @wxt-dev/module-solid v1.1.1 (#789) 2024-07-02 11:29:53 -05:00
GitHub Actions 5e361c0085 chore(release): @wxt-dev/module-solid v1.1.1 2024-07-02 16:23:54 +00:00
Aaron ccfc207e92 fix: Use prepare instead of postinstall for local dev setup (#788) 2024-07-02 11:18:44 -05:00
GitHub Actions 36be4ce771 chore(release): @wxt-dev/module-solid v1.1.0 2024-07-02 15:37:46 +00:00
KnightYoshi 18f8a387b0 feat: add eslint 9 config support (#762)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-07-02 10:32:32 -05:00
C.C 48c2c07557 docs: Fix manifest typo (#787) 2024-07-02 07:09:07 -05:00
Aaron 61548a9478 fix(dev): Catch error when attempting to reload a tab in a saved tab group (#786) 2024-07-01 16:11:00 -05:00
dependabot[bot] fd678e9572 chore(deps): bump oven-sh/setup-bun from 1 to 2 (#785)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 15:40:10 -05:00
Aaron 63fb913f76 docs: New example search (#751) 2024-06-30 09:01:42 -05:00
Aaron faffe20a62 chore(deps): Upgrade non-major deps (#778) 2024-06-29 15:00:15 -05:00
Aaron 3ed8bd10fd chore: Upgrade buildc to v1.0.10 (#780) 2024-06-29 14:09:56 -05:00
Asakura Mizu 6f3f270573 fix: Respect custom outDir when cleaning and zipping (#774) 2024-06-28 09:05:40 -05:00
Aaron 5347cb4626 chore: Replace consola with wxt.logger (#776) 2024-06-28 09:04:21 -05:00
Aaron d6b4d2d1f6 chore(dev-deps): Upgrade buildc to v1.0.9 2024-06-27 16:47:35 -05:00
Aaron 0d7ae287da chore(dev-deps): Upgrade buildc to v1.0.8 2024-06-27 16:38:40 -05:00
Aaron 90cb3bd3bf feat: Enable auto-imports for JSX/TSX files (#773) 2024-06-27 00:05:45 -05:00
Prakhar 🔱 Man of Creativity 60eb97c4b2 docs: Update examples URL (#768)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-06-26 13:52:10 -05:00
Aaron 0ffc6eae3e Fix comment typo 2024-06-25 15:12:59 -05:00
GitHub Actions 06a8c5fb0e chore(release): wxt v0.18.10 2024-06-25 20:03:59 +00:00
Aaron eb86413f35 chore: Consolidate unimport code into a built-in module (#771) 2024-06-25 14:59:28 -05:00
Aaron b29d49cbf1 fix: Await prepare:types hook 2024-06-24 10:04:44 -05:00
Aaron c0a0edb30c feat(modules): Allow adding generated public files (#769) 2024-06-24 08:46:45 -05:00
Aaron b15a933c75 feat: Add prepare:types hook to extend .wxt/ directory generation (#767) 2024-06-23 15:12:28 -05:00
Malix 1851be22a7 docs: Update CRXJS Firefox Compatibility (#764)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2024-06-23 10:32:09 -05:00
Aaron 39f6c29e4f chore: Refactor package manager test fixtures 2024-06-21 09:10:22 -05:00
GitHub Actions 936e89fbfb chore(release): wxt v0.18.9 2024-06-20 15:34:16 +00:00
Aaron ad365ecde4 feat(experimental): Replace viteRuntime option with entrypointImporter option, and implement vite-node importer (#761) 2024-06-20 10:27:19 -05:00
GitHub Actions b4283e37bd chore(release): wxt v0.18.8 2024-06-20 14:03:16 +00:00
Aaron b467ad760a fix(modules): addViteConfig ignored user vite config (#760) 2024-06-20 08:59:46 -05:00
Aaron 9fc6408ef9 chore: Add E2E test for addImportPreset 2024-06-20 08:52:45 -05:00
Aaron 394cb1a520 docs: Add 'WorkFlowy MultiFlow' to homepage 2024-06-19 14:16:58 -05:00
Aaron ffec88549b fix: Ignore .wxt file changes in dev (#755) 2024-06-19 08:33:52 -05:00
Ziheng Xun 4758bea187 docs: update sandbox.md (#754) 2024-06-19 08:06:08 -05:00
Aaron 6671471f75 feat(dev): Reload extension when public files change (#752) 2024-06-19 08:05:56 -05:00
Aaron ae78dc7fb3 chore: Refactor client web socket util (#753) 2024-06-18 22:47:30 -05:00
Aaron b62af3d007 chore(dev-deps): Upgrade @aklinker1/buildc to 1.0.7 (#748) 2024-06-18 00:11:14 -05:00
Aaron 43ae44ecd5 fix: Don't load plugins twice in HTML pages (#746) 2024-06-17 23:41:39 -05:00
Aaron 5bab9d2f59 chore: Fix build script flags (#745) 2024-06-17 23:41:10 -05:00
Aaron 7edf1c8103 fix: Upgrade wxt peer to >= 0.18.6
This closes https://github.com/wxt-dev/wxt/issues/743
2024-06-17 14:42:24 -05:00
Aaron 53b4eac955 docs: Update wxt init command for bun 2024-06-17 14:39:41 -05:00
GitHub Actions f9a10c2c52 chore(release): wxt v0.18.7 2024-06-17 16:17:42 +00:00
Aaron 8206ca9164 fix(zip): Revert dotfile changes from #674 (#742) 2024-06-17 11:10:06 -05:00
Aaron 1a823cc008 chore: Extract build cache script to NPM package (#737) 2024-06-17 11:09:18 -05:00
Aaron efc011eb81 ci: Fix release 422 failure 2024-06-14 00:36:03 -05:00
GitHub Actions 35f6d28977 chore(release): @wxt-dev/module-solid v1.0.1 2024-06-14 05:29:40 +00:00
Aaron 43fcbec0eb fix: Add target: esnext by default (#733) 2024-06-14 00:25:11 -05:00
Aaron a0d56b4d27 chore: Update templates to use new modules (#732) 2024-06-14 00:19:13 -05:00
Aaron 38ccb6ace6 docs: Update frontend framework docs with new modules (#731) 2024-06-14 00:06:23 -05:00
Aaron aaf75f1de1 ci: Only mark wxt releases as latest 2024-06-14 00:02:41 -05:00
Aaron 21e8ca0a2c chore: Add changelog 2024-06-13 23:52:58 -05:00
229 changed files with 4276 additions and 2629 deletions
+5 -4
View File
@@ -18,7 +18,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm -r --sequential build
- run: pnpm buildc all
build-demo:
runs-on: ubuntu-22.04
steps:
@@ -33,9 +33,9 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- uses: oven-sh/setup-bun@v1
- uses: oven-sh/setup-bun@v2
- name: pnpm test:coverage
run: pnpm test:coverage --reporter=default --reporter=hanging-process
run: pnpm test:coverage -- --reporter=default --reporter=hanging-process
- uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -44,10 +44,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: pnpm test run
- run: pnpm test
template:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
template:
- react
+5 -5
View File
@@ -1,5 +1,5 @@
<h1 align="center">
<img style="vertical-align:middle" width="44" src="./docs/public/hero-logo.svg" alt="WXT Logo">
<img style="vertical-align:middle" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo">
<span>WXT</span>
</h1>
@@ -22,18 +22,18 @@
</p>
<p align="center">
<a href="https://wxt.dev" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/guide/installation.html" target="_blank">Installation</a>
<a href="https://wxt.dev/guide/installation.html" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
&bull;
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
&bull;
<a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
&bull;
<a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
</p>
![Example CLI Output](./docs/assets/cli-output.png)
![Example CLI Output](https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/assets/cli-output.png)
## Demo
@@ -1,34 +0,0 @@
<script lang="ts" setup>
import { ref, onMounted, computed } from 'vue';
const props = defineProps<{
tag?: string;
}>();
const examples = ref();
onMounted(async () => {
const res = await fetch(
'https://raw.githubusercontent.com/wxt-dev/wxt-examples/main/examples.json',
);
examples.value = await res.json();
});
const filteredExamples = computed(() => {
if (props.tag == null) return examples.value;
return examples.value.filter((example) => {
return example.tags?.includes(props.tag);
});
});
</script>
<template>
<ul>
<li v-if="examples == null">Loading...</li>
<template v-else>
<li v-for="example of filteredExamples">
<a :href="example.url" target="_blank">{{ example.name }}</a>
</li>
</template>
</ul>
</template>
@@ -0,0 +1,226 @@
<script lang="ts" setup>
import { ref, onMounted, computed, toRaw, Ref } from 'vue';
import ExampleSearchFilterByItem from './ExampleSearchFilterByItem.vue';
import ExampleSearchResult from './ExampleSearchResult.vue';
import { ExamplesMetadata, KeySelectedObject } from '../utils/types';
const props = 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>
@@ -0,0 +1,100 @@
<script lang="ts" setup>
import { computed, toRaw } from 'vue';
import { KeySelectedObject } from '../utils/types';
const props = 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>
@@ -0,0 +1,72 @@
<script lang="ts" setup>
import { Example } from '../utils/types';
const props = 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 &rarr;</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>
@@ -29,6 +29,12 @@ const chromeExtensionIds = [
'nedcanggplmbbgmlpcjiafgjcpdimpea', // YTBlock - Block any content from YouTube™
'oadbjpccljkplmhnjekgjamejnbadlne', // demo.fun - Interactive product demos that convert
'iopdafdcollfgaoffingmahpffckmjni', // SmartEReply: Elevate Your LinkedIn™ Engagement with AI 🚀📈
'khjdmjcmpolknpccmaaipmidphjokhdf', // WorkFlowy MultiFlow
'fencadnndhdeggodopebjgdfdlhcimfk', // 香草布丁🌿🍮- https://github.com/Xdy1579883916/vanilla-pudding
'bnacincmbaknlbegecpioobkfgejlojp', // MaxFocus: Link Preview
'bcpgdpedphodjcjlminjbdeejccjbimp', // 汇率转换-中文版本
'loeilaonggnalkaiiaepbegccilkmjjp', // Currency Converter Plus
'npcnninnjghigjfiecefheeibomjpkak', // Respond Easy
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
+1 -1
View File
@@ -78,7 +78,6 @@ export default defineConfig({
menuItem('Configuration', 'configuration'),
menuItem('Entrypoints', 'entrypoints'),
menuItem('Assets', 'assets'),
menuItem('Testing', 'testing'),
menuItem('Publishing', 'publishing'),
menuItem('Migrate to WXT', 'migrate-to-wxt'),
menuItem('Compare', 'compare'),
@@ -122,6 +121,7 @@ export default defineConfig({
// Files
menuItem('.env', 'env'),
menuItem('app.config.ts', 'app-config'),
menuItem('package.json', 'package'),
menuItem('tsconfig.json', 'tsconfig'),
menuItem('web-ext.config.ts', 'web-ext-config'),
+2 -2
View File
@@ -2,7 +2,7 @@ import DefaultTheme from 'vitepress/theme';
import Icon from '../components/Icon.vue';
import EntrypointPatterns from '../components/EntrypointPatterns.vue';
import UsingWxtSection from '../components/UsingWxtSection.vue';
import ExampleList from '../components/ExampleList.vue';
import ExampleSearch from '../components/ExampleSearch.vue';
import './custom.css';
export default {
@@ -11,6 +11,6 @@ export default {
ctx.app.component('Icon', Icon);
ctx.app.component('EntrypointPatterns', EntrypointPatterns);
ctx.app.component('UsingWxtSection', UsingWxtSection);
ctx.app.component('ExampleList', ExampleList);
ctx.app.component('ExampleSearch', ExampleSearch);
},
};
+18
View File
@@ -0,0 +1,18 @@
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>;
+16 -4
View File
@@ -1,7 +1,19 @@
# Examples
---
layout: page
---
Simple walkthroughs to accomplish common tasks or patterns with WXT.
<style>
.examples-container {
padding: 32px;
}
</style>
<ExampleList />
<div class="examples-container">
<div class="vp-doc">
<h1>Examples</h1>
</div>
> Full code available at [`wxt-dev/wxt-examples`](https://github.com/wxt-dev/wxt-examples)
<br />
<ExampleSearch />
</div>
+26 -26
View File
@@ -4,30 +4,30 @@ Lets compare the features of WXT vs [Plasmo](https://docs.plasmo.com/framework)
## Overview
| Features | WXT | Plasmo | CRXJS |
| ---------------------------------------------------- | :-------------------------: | :--------------------------------------: | :---------------------------------------------------------------------: |
| Supports all browsers | ✅ | ✅ | ❌ See [#56](https://github.com/crxjs/chrome-extension-tools/issues/56) |
| MV2 Support | ✅ | ✅ | 🟡 Either MV2 or MV3 |
| MV3 Support | ✅ | ✅ | 🟡 Either MV2 or MV3 |
| Create Extension ZIPs | ✅ | ✅ | |
| Create Firefox Sources ZIP | ✅ | ❌ | |
| First-class TypeScript support | ✅ | ✅ | |
| Entrypoint discovery | File based | File based | |
| Inline entrypoint config | ✅ | ✅ | Manifest based |
| Auto-imports | ✅ | ❌ | |
| Supports all frontend frameworks | ✅ | 🟡 Only React, Vue, and Svelte | |
| Framework specific entrypoints (like `Popup.tsx`) | 🟡 `.html` `.ts` `.tsx` | ✅ `.html` `.ts` `.tsx` `.vue` `.svelte` | |
| Automated publishing | ✅ | ✅ | |
| Remote Code Bundling (Google Analytics) | ✅ | ✅ | |
| Features | WXT | Plasmo | CRXJS |
| ---------------------------------------------------- | :-------------------------: | :--------------------------------------: | :-------------------------------------: |
| Supports all browsers | ✅ | ✅ | ✅ As of `v2.0.0-beta.23` |
| MV2 Support | ✅ | ✅ | 🟡 Either MV2 or MV3 |
| MV3 Support | ✅ | ✅ | 🟡 Either MV2 or MV3 |
| Create Extension ZIPs | ✅ | ✅ | |
| Create Firefox Sources ZIP | ✅ | ❌ | |
| First-class TypeScript support | ✅ | ✅ | |
| Entrypoint discovery | File based | File based | |
| Inline entrypoint config | ✅ | ✅ | Manifest based |
| Auto-imports | ✅ | ❌ | |
| Supports all frontend frameworks | ✅ | 🟡 Only React, Vue, and Svelte | |
| Framework specific entrypoints (like `Popup.tsx`) | 🟡 `.html` `.ts` `.tsx` | ✅ `.html` `.ts` `.tsx` `.vue` `.svelte` | |
| Automated publishing | ✅ | ✅ | |
| Remote Code Bundling (Google Analytics) | ✅ | ✅ | |
| <strong style="opacity: 50%">Dev Mode</strong> | | |
| `.env` Files | ✅ | ✅ | |
| Opens browser and install extension | ✅ | ❌ | |
| HMR for UIs | ✅ | 🟡 React only | |
| Reload HTML Files on Change | ✅ | 🟡 Reloads entire extension | |
| Reload Content Scripts on Change | ✅ | 🟡 Reloads entire extension | |
| Reload Background on Change | 🟡 Reloads entire extension | 🟡 Reloads entire extension | 🟡 Reloads entire extension |
| Respects Content Script `run_at` | ✅ | ✅ | ❌ ESM-style loaders run asynchronously |
| <strong style="opacity: 50%">Built-in Utils</strong> | | | |
| Storage | ✅ | ✅ | |
| Messaging | ❌ | ✅ | |
| Content Script UI | ✅ | ✅ | |
| `.env` Files | ✅ | ✅ | |
| Opens browser and install extension | ✅ | ❌ | |
| HMR for UIs | ✅ | 🟡 React only | |
| Reload HTML Files on Change | ✅ | 🟡 Reloads entire extension | |
| Reload Content Scripts on Change | ✅ | 🟡 Reloads entire extension | |
| Reload Background on Change | 🟡 Reloads entire extension | 🟡 Reloads entire extension | 🟡 Reloads entire extension |
| Respects Content Script `run_at` | ✅ | ✅ | ❌ ESM-style loaders run asynchronously |
| <strong style="opacity: 50%">Built-in Utils</strong> | | | |
| Storage | ✅ | ✅ | |
| Messaging | ❌ | ✅ | |
| Content Script UI | ✅ | ✅ | |
+1 -1
View File
@@ -17,7 +17,7 @@ In WXT, you create an entrypoint by adding a file to the `entrypoints/` director
Some entrypoint filesname patterns are reserved by WXT and effect how the manifest is generated.
- `popup` adds a `action` to the manifest
- `popup` adds an `action` to the manifest
- `background` adds a background script/service worker
- `*.content.ts` adds a content script
- ...
+9 -3
View File
@@ -15,7 +15,14 @@ npx wxt@latest init <project-name>
```
```sh [bun]
bunx wxt@latest init <project-name>
# The "wxt init" command currently fails when ran with bunx.
# Use NPX as a workaround, and select "bun" as your package
# manager. To stay up to date with this issue, follow
# https://github.com/wxt-dev/wxt/issues/707
#
# bunx wxt@latest init <project-name>
npx wxt@latest init <project-name>
```
:::
@@ -96,7 +103,6 @@ pnpm dev
```
:::tip 🎉&ensp;Well done!
The dev command will build the extension for development, open the browser, and reload the different parts of the extension when you save changes.
:::
@@ -107,4 +113,4 @@ You're ready to build your web extension!
- Read the rest of the "Get Started" pages for a high-overview of what WXT can do
- Read the [Guide](/guide/key-concepts/manifest) to learn in-depth about each feature WXT supports
- [Configure WXT](./configuration) by creating a `wxt.config.ts` file
- Checkout [example projects](https://github.com/wxt-dev/wxt-examples) to see how to perform common tasks with WXT
- Checkout [example projects](https://github.com/wxt-dev/examples) to see how to perform common tasks with WXT
+1 -1
View File
@@ -25,7 +25,7 @@ In general, you'll need to:
&ensp;<input type="checkbox" /> Convert custom import syntax to be compatible with Vite<br />
&ensp;<input type="checkbox" /> Add a default export to JS entrypoints<br />
&ensp;<input type="checkbox" /> Use the `browser` global instead of `chrome`<br />
&ensp;<input type="checkbox" /> Compare final `manifest.json` files, making sure permissions and host permissions are unchanged<br />
&ensp;<input type="checkbox" /> Compare final `manifest.json` files, making sure permissions and host permissions are unchanged. Use [Google's update testing tool](https://github.com/GoogleChromeLabs/extension-update-testing-tool) if your extension is already live on the Chrome Web Store<br />
&ensp;<input type="checkbox" /> Extension output by `wxt build` works the same way as before the migration<br />
Every project is different, so there's no one-solution-fits-all to migrating your project. Just make sure `wxt dev` runs, `wxt build` results in a working extension, and the list of permissions in the `manifest.json` hasn't changed. If all that looks good, you've finished migrating your extension!
-25
View File
@@ -1,25 +0,0 @@
# Testing
## Official Frameworks
WXT officially supports [Vitest](https://vitest.dev/) for unit tests and either [Playwright](https://playwright.dev/) or [Puppeteer](https://pptr.dev/) for E2E tests against Chromium browsers.
For details setting up each testing framework, see the official examples:
<ExampleList tag="testing" />
## Unofficial Frameworks
Puppeteer and Playwright are the only E2E test runners that support Chrome Extensions. There are no other options at the time of writing.
There are other options for unit tests however, like [Jest](https://jestjs.io/), [Mocha](https://mochajs.org/), or [`node:test`](https://nodejs.org/api/test.html). **_WXT does not claim to support any of them_** because none of them support all of WXT's features, like TypeScript or auto-imports.
If you want to try to use a different framework for unit tests, you will need to configure the environment manually:
- **Auto-imports**: Add `unimport` to your test environment or disable them by setting `imports: false` in your `wxt.config.ts` file
- **`browser` mock**: Mock the `webextension-polyfill` module globally with `wxt/dist/virtual/mock-browser.js`
- **[Remote Code Bundling](/guide/go-further/remote-code)**: If you use it, configure your environment to handle the `url:` module prefix
- **Global Variables**: If you consume them, manually define globals provided by WXT (like `import.meta.env.BROWSER`) by adding them to the global scope before accessing them (`import.meta.env.BROWSER = "chrome"`)
- **Import paths**: If you use the `@/` or `~/` path aliases, add them to your test environment
[Here's how Vitest is configured](https://github.com/wxt-dev/wxt/blob/main/packages/wxt/src/testing/wxt-vitest-plugin.ts) for reference.
@@ -0,0 +1,61 @@
# `<srcDir>/app.config.ts`
:::warning Nuxt Users
If you're familiar with Nuxt, this file is meant to be a direct equivalent to Nuxt's `app.config.ts` file.
However, some of Nuxt's features, like overriding the app config based on a `.env` file or automatically generating the config's types, are not implemented. They are planned, just not implemented yet. Feel free to open a PR!
:::
## Overview
Define runtime configuration in a single place.
```ts
// <srcDir>/app.config.ts
import { defineAppConfig } from 'wxt/sandbox';
// Define types for your config
declare module 'wxt/sandbox' {
export interface WxtAppConfig {
theme?: 'light' | 'dark';
}
}
export default defineAppConfig({
theme: 'dark',
});
```
Then access the config in your extension by calling `useAppConfig`:
```ts
console.log(useAppConfig()); // { theme: "dark" }
```
## Environment Variables
If you have a `.env` file, you can access any variables defined in it here. You can convert them to better types (like booleans), add types for them, or leave them as is.
```txt
# .env
VITE_BUG_REPORTING_DISABLED=true
VITE_API_KEY=...
```
```ts
// <srcDir>/app.config.ts
declare module 'wxt/sandbox' {
export interface WxtAppConfig {
bugReportingDisabled: boolean;
apiKey?: string;
}
}
export default defineAppConfig({
bugReportingDisabled: process.env.VITE_BUG_REPORTING_DISABLED === 'true',
apiKey: process.env.VITE_API_KEY,
});
```
> You don't have to do this, you can use `process.env.VITE_*` anywhere in your runtime code, but putting them here consolidates them to one place and defines what variables are expected.
@@ -37,4 +37,4 @@ Chrome extensions allow you to add panels and side panes to the devtools window.
See the WXT's examples for a full walkthrough of extending the devtools window:
<ExampleList tag="devtools" />
- [Devtools Setup](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-devtools#readme)
@@ -12,8 +12,8 @@ Firefox does not support sandboxed pages.
:patterns="[
['sandbox.html', 'sandbox.html'],
['sandbox/index.html', 'sandbox.html'],
['<name>.sandbox.html', '<name>.html` '],
['<name>.sandbox/index.html', '<name>.html` '],
['<name>.sandbox.html', '<name>.html'],
['<name>.sandbox/index.html', '<name>.html'],
]"
/>
+4 -2
View File
@@ -6,7 +6,9 @@ WXT officially supports [Vitest](https://vitest.dev/) for unit tests and either
For details setting up each testing framework, see the official examples:
<ExampleList tag="testing" />
- [Vitest](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-vitest#readme)
- [Playwright](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-playwright#readme)
- [Puppeteer](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-puppeteer#readme)
### Unofficial Frameworks
@@ -17,7 +19,7 @@ There are other options for unit tests however, like [Jest](https://jestjs.io/),
If you want to try to use a different framework for unit tests, you will need to configure the environment manually:
- **Auto-imports**: Add `unimport` to your test environment or disable them by setting `imports: false` in your `wxt.config.ts` file
- **`browser` mock**: Mock the `webextension-polyfill` module globally with `wxt/dist/virtual/mock-browser.js`
- **`browser` mock**: Mock the `webextension-polyfill` module globally with `wxt/dist/virtual/mock-browser.mjs`
- **[Remote Code Bundling](/guide/go-further/remote-code)**: If you use it, configure your environment to handle the `url:` module prefix
- **Global Variables**: If you consume them, manually define globals provided by WXT (like `import.meta.env.BROWSER`) by adding them to the global scope before accessing them (`import.meta.env.BROWSER = "chrome"`)
- **Import paths**: If you use the `@/` or `~/` path aliases, add them to your test environment
+7 -3
View File
@@ -18,9 +18,7 @@ export default defineConfig({
## Using Plugins
Plugins can be passed into the `vite` configuration in you `wxt.config.ts` file, just like any other option.
All plugins should work in WXT, but it is worth pointing out that since WXT orchestrates multiple vite builds to bundle an extension, plugins will be executed multiple times if necessary.
Plugins can be passed into the `vite` configuration in your `wxt.config.ts` file, just like any other option.
```ts
import { defineConfig } from 'wxt';
@@ -33,3 +31,9 @@ export default defineConfig({
}),
});
```
:::warning UNEXPECTED BEHAVIOR
Due to the way WXT orchestrates Vite builds, some plugins may not work as expected. Search [GitHub issues](https://github.com/wxt-dev/wxt/issues?q=is%3Aissue+label%3A%22vite+plugin%22) if you run into issues with a specific plugin.
If one doesn't exist, please open a [new issue](https://github.com/wxt-dev/wxt/issues/new/choose)!
:::
+27 -12
View File
@@ -6,7 +6,7 @@ WXT uses the same tool as Nuxt for auto-imports, [`unimport`](https://github.com
Some WXT APIs can be used without importing them:
- [`browser`](/api/reference/wxt/browser/variables/browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
- [`browser`](/api/reference/wxt/browser/#browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
- [`defineContentScript`](/api/reference/wxt/sandbox/functions/defineContentScript) from `wxt/sandbox`
- [`defineBackground`](/api/reference/wxt/sandbox/functions/defineBackground) from `wxt/sandbox`
- [`defineUnlistedScript`](/api/reference/wxt/sandbox/functions/defineUnlistedScript) from `wxt/sandbox`
@@ -79,24 +79,39 @@ export default defineConfig({
## ESLint
ESLint doesn't understand auto-imports; it thinks all auto-imported variables are undeclared globals and will report lint errors for each. To fix this, extend the ESLint file generated inside the `.wxt` directory:
```js
// .eslintrc.js
module.exports = {
extends: ['./.wxt/eslintrc-auto-import.json'],
};
```
By default, this file will be generated when ESLint is a direct dependency. If ESLint is a subdependency or your project is a monorepo, it may not be generated automatically. In this case, you can tell WXT to generate it:
ESLint doesn't know about the auto-imported variables unless they are explicitly defined in the `globals` config. By default, WXT will generate the config if it detects ESLint is installed in your project. If the config isn't generated automatically, you can manually tell WXT to generate it.
```ts
// wxt.config.ts
export default defineConfig({
imports: {
eslintrc: {
enabled: true,
enabled: 8, // Generate ESLint v8 compatible config
// or
enabled: 9, // Generate ESLint v9 compatible config
},
},
});
```
### ESLint 9 and above
WXT supports the new "flat config" file format. Just import the generated file and add it to the array of config to extend.
```js
// eslint.config.mjs
import autoImports from './.wxt/eslint-auto-imports.mjs';
export default [autoImports];
```
### ESLint 8 and below
Just extend the generated file:
```js
// .eslintrc.mjs
export default {
extends: ['./.wxt/eslintrc-auto-import.json'],
};
```
+50 -30
View File
@@ -1,28 +1,59 @@
# Frontend Frameworks
WXT supports all frontend frameworks with a Vite plugin:
## Built-in Modules
- `@vitejs/plugin-vue`
- `@vitejs/plugin-react`
- `@vitejs/plugin-react-swc`
- And more!
WXT has preconfigured modules for 4 frameworks:
Just add the vite plugin to your config and you're good to go! Use the framework in HTML pages or content scripts, it will just work 👍
- [`@wxt-dev/module-react`](https://github.com/wxt-dev/wxt/tree/main/packages/module-react)
- [`@wxt-dev/module-vue`](https://github.com/wxt-dev/wxt/tree/main/packages/module-vue)
- [`@wxt-dev/module-svelte`](https://github.com/wxt-dev/wxt/tree/main/packages/module-svelte)
- [`@wxt-dev/module-solid`](https://github.com/wxt-dev/wxt/tree/main/packages/module-solid)
Install the module for your framework, then add it to your config:
:::code-group
```ts [Vue]
```ts [React]
import { defineConfig } from 'wxt';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
vite: () => ({
plugins: [vue()],
}),
modules: ['@wxt-dev/module-react'],
});
```
```ts [React]
```ts [Vue]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-vue'],
});
```
```ts [Svelte]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-svelte'],
});
```
```ts [Solid]
import { defineConfig } from 'wxt';
export default defineConfig({
modules: ['@wxt-dev/module-solid'],
});
```
:::
## Adding Vite Plugins
If your framework doesn't have an official WXT module, no worries! WXT supports any framework with a Vite plugin.
Just add the Vite plugin to your config and you're good to go! Use the framework in HTML pages or content scripts, it will just work 👍
```ts
import { defineConfig } from 'wxt';
import react from '@vitejs/plugin-react';
@@ -33,18 +64,7 @@ export default defineConfig({
});
```
```ts [Svelte]
import { defineConfig } from 'wxt';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
vite: () => ({
plugins: [svelte()],
}),
});
```
:::
The WXT modules just simplify the configuration and add auto-imports. They're not much different than the above.
## Multiple Apps
@@ -75,7 +95,7 @@ Usually, this means each entrypoint should be a directory with it's own files in
├ App.vue
├ main.ts
├ style.css
└ rotuer.ts
└ router.ts
```
## Configuring Routers
@@ -84,9 +104,9 @@ Lots of frameworks come with routers for building a multi-page app using the URL
Instead, you need to configure the router to run in "hash" mode, where the routing information is apart of the URL's hash, not the path (ie: `popup.html#/` and `popup.html#/account/settings`)
Refer to your router's docs for information about "hash" mode and how to enable it. Here's a non-extensive list of a few popular routers:
Refer to your router's docs for information about hash mode and how to enable it. Here's a non-extensive list of a few popular routers:
- [React](https://reactrouter.com/en/main/routers/create-hash-router)
- [Vue](https://router.vuejs.org/guide/essentials/history-mode.html#Hash-Mode)
- [Svelte](https://www.npmjs.com/package/svelte-spa-router#hash-based-routing)
- [Solid](https://github.com/solidjs/solid-router?tab=readme-ov-file#hash-mode-router)
- [`react-router`](https://reactrouter.com/en/main/routers/create-hash-router)
- [`vue-router`](https://router.vuejs.org/guide/essentials/history-mode.html#Hash-Mode)
- [`svelte-spa-router`](https://www.npmjs.com/package/svelte-spa-router#hash-based-routing)
- [`solid-router`](https://github.com/solidjs/solid-router?tab=readme-ov-file#hash-mode-router)
+29 -3
View File
@@ -25,7 +25,7 @@ Here's an example `wxt.config.ts` file:
import { defineConfig } from 'wxt';
export default defineConfig({
mainfest: {
manifest: {
action: {
default_title: 'Some Title',
},
@@ -147,6 +147,31 @@ export default defineConfig({
});
```
### Host Permissions
[Host Permissions](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#host-permissions) must be listed in the manifest config.
```ts
export default defineConfig({
manifest: {
host_permissions: ['*://*.google.com/*'],
},
});
```
:::warning
If you use host permissions and target both MV2 and MV3, make sure to only include the required host permissions for each version:
```ts
export default defineConfig({
manifest: ({ manifestVersion }) => ({
host_permissions: manifestVersion === 2 ? [...] : [...],
}),
});
```
:::
## Localization
Similar to the icon, the [`_locales` directory](https://developer.chrome.com/docs/extensions/reference/i18n/) should be placed inside the the WXT's [`public` directory](/guide/directory-structure/public/).
@@ -176,7 +201,8 @@ export default defineConfig({
See the official localization examples for more details:
<ExampleList tag="i18n" />
- [I18n](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vanilla-i18n#readme)
- [Vue I18n](https://github.com/wxt-dev/wxt-examples/tree/main/examples/vue-i18n#readme)
## Actions
@@ -228,7 +254,7 @@ The `manifest` option can also be set equal to a function, letting you use logic
```ts
// wxt.config.ts
export default defineConfig({
manifest: ({ manifestVersion, browser, mode, comamnd }) => {
manifest: ({ manifestVersion, browser, mode, command }) => {
return { ... }
}
})
+13 -15
View File
@@ -5,44 +5,42 @@
"node": ">=18.20.3",
"pnpm": "^8.6.3"
},
"packageManager": "pnpm@8.15.8",
"packageManager": "pnpm@8.15.9",
"scripts": {
"check": "check && pnpm -r --sequential run check",
"test": "vitest",
"test:coverage": "vitest run --coverage.enabled \"--coverage.include=packages/wxt/src/**\" \"--coverage.exclude=packages/wxt/src/core/utils/testing/**\" \"--coverage.exclude=**/*.d.ts\" \"--coverage.exclude=**/fixtures/**\"",
"test": "pnpm -r --sequential run test run",
"test:coverage": "pnpm -r --sequential run test:coverage",
"prepare": "simple-git-hooks",
"prepublish": "pnpm -s build",
"docs:gen": "typedoc --options docs/typedoc.json",
"docs:dev": "pnpm -s docs:gen && vitepress dev docs",
"docs:build": "pnpm -s docs:gen && vitepress build docs",
"docs:preview": "pnpm -s docs:gen && vitepress preview docs",
"build-deps": "pnpm tsx scripts/build-deps.ts"
"docs:preview": "pnpm -s docs:gen && vitepress preview docs"
},
"devDependencies": {
"@aklinker1/buildc": "^1.0.11",
"@aklinker1/check": "^1.3.1",
"@types/fs-extra": "^11.0.4",
"@vitest/coverage-v8": "^1.6.0",
"@vitest/coverage-v8": "^2.0.0",
"changelogen": "^0.5.5",
"consola": "^3.2.3",
"dependency-graph": "^1.0.0",
"execa": "^9.2.0",
"execa": "^9.3.0",
"fast-glob": "^3.3.2",
"fs-extra": "^11.2.0",
"hasha": "^6.0.0",
"lint-staged": "^15.2.7",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.2",
"prettier": "^3.3.3",
"simple-git-hooks": "^2.11.1",
"tsx": "^4.15.4",
"tsx": "4.15.7",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "4.0.0-next.23",
"typedoc-vitepress-theme": "1.0.0-next.3",
"typescript": "^5.4.5",
"vitepress": "^1.2.3",
"vitest": "^1.6.0",
"vitest-mock-extended": "^1.3.1",
"vitest-plugin-random-seed": "^1.1.0",
"vue": "^3.4.27",
"typescript": "^5.5.3",
"vitepress": "^1.3.1",
"vitest-mock-extended": "^1.3.2",
"vue": "^3.4.33",
"wxt": "workspace:*",
"yaml": "^2.4.5"
},
+24
View File
@@ -0,0 +1,24 @@
# Changelog
## v1.1.0
[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.0.0...module-react-v1.1.0)
### 🚀 Enhancements
- Enable auto-imports for JSX/TSX files ([#773](https://github.com/wxt-dev/wxt/pull/773))
### 🩹 Fixes
- Upgrade wxt peer to >= 0.18.6 ([7edf1c8](https://github.com/wxt-dev/wxt/commit/7edf1c8))
- Use `prepare` instead of `postinstall` for local dev setup ([#788](https://github.com/wxt-dev/wxt/pull/788))
### 🏡 Chore
- Add changelog ([21e8ca0](https://github.com/wxt-dev/wxt/commit/21e8ca0))
- Extract build cache script to NPM package ([#737](https://github.com/wxt-dev/wxt/pull/737))
- **deps:** Upgrade non-major deps ([#778](https://github.com/wxt-dev/wxt/pull/778))
## v1.0.0
Initial release 🎉
+12
View File
@@ -0,0 +1,12 @@
import { defineBuildConfig } from 'unbuild';
import { resolve } from 'node:path';
export default defineBuildConfig({
rootDir: 'modules',
outDir: resolve(__dirname, 'dist'),
entries: [{ input: 'react.ts', name: 'index' }],
rollup: {
emitCJS: true,
},
declaration: true,
});
+5
View File
@@ -0,0 +1,5 @@
export default function () {
const [count, setCount] = useState(0);
const increment = () => setCount((count) => count + 1);
return <button onClick={increment}>Count: {count}</button>;
}
@@ -0,0 +1,33 @@
import { defineContentScript } from 'wxt/sandbox';
import { ContentScriptContext, createShadowRootUi } from 'wxt/client';
import React from 'react';
import ReactDOM from 'react-dom/client';
export default defineContentScript({
matches: ['*://*/*'],
async main(ctx) {
const ui = await createUi(ctx);
ui.mount();
},
});
function createUi(ctx: ContentScriptContext) {
return createShadowRootUi(ctx, {
name: 'react-ui',
position: 'inline',
append: 'first',
onMount(container) {
const root = ReactDOM.createRoot(container);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
return root;
},
onRemove(root) {
root?.unmount();
},
});
}
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
const root = document.getElementById('app')!;
ReactDOM.createRoot(root).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
@@ -9,10 +9,22 @@ export default defineWxtModule<ReactModuleOptions>({
const { vite } = options ?? {};
addViteConfig(wxt, () => ({
// @ts-expect-error: Vite version conflicts
plugins: [react(vite)],
}));
addImportPreset(wxt, 'react');
// Enable auto-imports for JSX files
wxt.hooks.hook('ready', (wxt) => {
if (wxt.config.imports === false) return;
wxt.config.imports.dirsScanOptions ??= {};
wxt.config.imports.dirsScanOptions.filePatterns = [
// Default plus JSX/TSX
'*.{ts,js,mjs,cjs,mts,cts,jsx,tsx}',
];
});
},
});
+12 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@wxt-dev/module-react",
"version": "1.0.0",
"version": "1.1.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -21,20 +21,25 @@
"dist"
],
"scripts": {
"build": "pnpm -s build-deps && unbuild",
"check": "pnpm -s build-deps && check",
"build-deps": "pnpm -ws build-deps @wxt-dev/module-react"
"dev": "wxt",
"check": "buildc --deps-only -- check",
"build": "buildc -- unbuild",
"prepare": "buildc --deps-only -- wxt prepare"
},
"peerDependencies": {
"wxt": ">=0.18.5"
"wxt": ">=0.18.6"
},
"dependencies": {
"@vitejs/plugin-react": "^4.3.1"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"publint": "^0.2.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "^5.5.3",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
View File
+5 -1
View File
@@ -1,4 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"extends": ["../../tsconfig.base.json", "./.wxt/tsconfig.json"],
"compilerOptions": {
"allowImportingTsExtensions": true,
"jsx": "react-jsx"
},
"exclude": ["node_modules/**", "dist/**"]
}
+42
View File
@@ -0,0 +1,42 @@
# Changelog
## v1.1.1
[compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.1.0...module-solid-v1.1.1)
### 🩹 Fixes
- Use `prepare` instead of `postinstall` for local dev setup ([#788](https://github.com/wxt-dev/wxt/pull/788))
## v1.1.0
[compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.0.1...module-solid-v1.1.0)
### 🚀 Enhancements
- Enable auto-imports for JSX/TSX files ([#773](https://github.com/wxt-dev/wxt/pull/773))
### 🩹 Fixes
- Upgrade wxt peer to >= 0.18.6 ([7edf1c8](https://github.com/wxt-dev/wxt/commit/7edf1c8))
### 🏡 Chore
- Extract build cache script to NPM package ([#737](https://github.com/wxt-dev/wxt/pull/737))
- **deps:** Upgrade non-major deps ([#778](https://github.com/wxt-dev/wxt/pull/778))
## v1.0.1
[compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.0.0...module-solid-v1.0.1)
### 🩹 Fixes
- Add `target: esnext` by default ([#733](https://github.com/wxt-dev/wxt/pull/733))
### 🏡 Chore
- Add changelog ([21e8ca0](https://github.com/wxt-dev/wxt/commit/21e8ca0))
## v1.0.0
Initial release 🎉
+1 -1
View File
@@ -4,7 +4,7 @@ Enables the use of [SolidJS](https://www.solidjs.com/) in your web extension, in
This plugin makes a few changes:
1. Adds `vite-plugin-solid` to vite
1. Adds `vite-plugin-solid` to and sets `build.target: esnext` in the vite config
2. Adds the [`solid-js` preset](https://github.com/unjs/unimport/blob/main/src/presets/solid.ts) to auto-imports
## Usage
+12
View File
@@ -0,0 +1,12 @@
import { defineBuildConfig } from 'unbuild';
import { resolve } from 'node:path';
export default defineBuildConfig({
rootDir: 'modules',
outDir: resolve(__dirname, 'dist'),
entries: [{ input: 'solid.ts', name: 'index' }],
rollup: {
emitCJS: true,
},
declaration: true,
});
+7
View File
@@ -0,0 +1,7 @@
import { Component } from 'solid-js';
export const App: Component = () => {
const [count, setCount] = createSignal(0);
const increment = () => setCount((count) => count + 1);
return <button onClick={increment}>Count: {count()}</button>;
};
@@ -0,0 +1,26 @@
import { defineContentScript } from 'wxt/sandbox';
import { ContentScriptContext, createShadowRootUi } from 'wxt/client';
import { render } from 'solid-js/web';
export default defineContentScript({
matches: ['*://*/*'],
async main(ctx) {
const ui = await createUi(ctx);
ui.mount();
},
});
function createUi(ctx: ContentScriptContext) {
return createShadowRootUi(ctx, {
name: 'solid-ui',
position: 'inline',
append: 'first',
onMount(container) {
return render(() => <App />, container);
},
onRemove(unmount) {
unmount?.();
},
});
}
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
@@ -0,0 +1,5 @@
import { render } from 'solid-js/web';
const root = document.getElementById('app')!;
render(() => <App />, root);
@@ -9,10 +9,25 @@ export default defineWxtModule<SolidModuleOptions>({
const { vite } = options ?? {};
addViteConfig(wxt, () => ({
// @ts-expect-error: Vite version mismatch
plugins: [solid(vite)],
build: {
target: 'esnext',
},
}));
addImportPreset(wxt, 'solid-js');
// Enable auto-imports for JSX files
wxt.hooks.hook('ready', (wxt) => {
if (wxt.config.imports === false) return;
wxt.config.imports.dirsScanOptions ??= {};
wxt.config.imports.dirsScanOptions.filePatterns = [
// Default plus JSX/TSX
'*.{ts,js,mjs,cjs,mts,cts,jsx,tsx}',
];
});
},
});
+9 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@wxt-dev/module-solid",
"version": "1.0.0",
"version": "1.1.1",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -21,20 +21,22 @@
"dist"
],
"scripts": {
"build": "pnpm -s build-deps && unbuild",
"check": "pnpm -s build-deps && check",
"build-deps": "pnpm -ws build-deps @wxt-dev/module-solid"
"dev": "wxt",
"check": "buildc --deps-only -- check",
"build": "buildc -- unbuild",
"prepare": "buildc --deps-only -- wxt prepare"
},
"peerDependencies": {
"wxt": ">=0.18.5"
"wxt": ">=0.18.6"
},
"dependencies": {
"vite-plugin-solid": "^2.10.2"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"publint": "^0.2.9",
"solid-js": "^1.8.18",
"typescript": "^5.5.3",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
View File
+5 -1
View File
@@ -1,4 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"extends": ["../../tsconfig.base.json", "./.wxt/tsconfig.json"],
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
},
"exclude": ["node_modules/**", "dist/**"]
}
+5
View File
@@ -0,0 +1,5 @@
# Changelog
## v1.0.0
Initial release 🎉
+1 -1
View File
@@ -5,7 +5,7 @@ Enables the use of [Svelte](https://svelte.dev/) in your web extension, in HTML
This plugin makes a few changes:
1. Adds `@sveltejs/vite-plugin-svelte` to vite
2. Adds the [`svelte` preset](https://github.com/unjs/unimport/blob/main/src/presets/vue.ts) to auto-imports
2. Adds the [`svelte` preset](https://github.com/unjs/unimport/blob/main/src/presets/svelte.ts) to auto-imports
## Usage
+5 -6
View File
@@ -21,20 +21,19 @@
"dist"
],
"scripts": {
"build": "pnpm -s build-deps && unbuild",
"check": "pnpm -s build-deps && check",
"build-deps": "pnpm -ws build-deps @wxt-dev/module-svelte"
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
},
"peerDependencies": {
"wxt": ">=0.18.5"
"wxt": ">=0.18.6"
},
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.1"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"publint": "^0.2.9",
"typescript": "^5.5.3",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
+1
View File
@@ -14,6 +14,7 @@ export default defineWxtModule<SvelteModuleOptions>({
addViteConfig(wxt, () => ({
plugins: [
// @ts-expect-error: Vite version mismatch
svelte({
// Using a svelte.config.js file causes a segmentation fault when importing the file
configFile: false,
+5
View File
@@ -0,0 +1,5 @@
# Changelog
## v1.0.0
Initial release 🎉
+5 -6
View File
@@ -21,20 +21,19 @@
"dist"
],
"scripts": {
"build": "pnpm -s build-deps && unbuild",
"check": "pnpm -s build-deps && check",
"build-deps": "pnpm -ws build-deps @wxt-dev/module-vue"
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- check"
},
"peerDependencies": {
"wxt": ">=0.18.5"
"wxt": ">=0.18.6"
},
"dependencies": {
"@vitejs/plugin-vue": "^5.0.5"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"publint": "^0.2.8",
"typescript": "^5.4.5",
"publint": "^0.2.9",
"typescript": "^5.5.3",
"unbuild": "^2.0.0",
"wxt": "workspace:*"
}
+12
View File
@@ -0,0 +1,12 @@
import autoImports from './.wxt/eslintrc-auto-import.js';
export default [
{
languageOptions: {
globals: {
...autoImports.globals,
},
sourceType: 'module',
},
},
];
+15 -10
View File
@@ -4,28 +4,33 @@
"private": true,
"type": "module",
"scripts": {
"dev": "pnpm -s build-deps && wxt",
"build": "pnpm -s build-deps && wxt build",
"build:all": "pnpm -s build-deps && run-s -s 'build:all:*'",
"dev": "buildc --deps-only -- wxt",
"build": "buildc --deps-only -- wxt build",
"build:all": "buildc --deps-only -- 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 -s build-deps && vitest",
"zip": "pnpm -s build-deps && wxt zip",
"check": "pnpm -s build-deps && check",
"postinstall": "pnpm -s build-deps && wxt prepare",
"build-deps": "pnpm -sw build-deps wxt-demo"
"test": "buildc --deps-only -- vitest",
"zip": "buildc --deps-only -- wxt zip",
"check": "buildc --deps-only -- check",
"postinstall": "buildc --deps-only -- wxt prepare"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/chrome": "^0.0.268",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"sass": "^1.77.5",
"typescript": "^5.4.5",
"sass": "^1.77.8",
"typescript": "^5.5.3",
"vitest": "^2.0.3",
"vitest-plugin-random-seed": "^1.1.0",
"wxt": "workspace:*"
},
"buildc": {
"outDir": ".output/chrome-mv3"
}
}
+11
View File
@@ -0,0 +1,11 @@
import { defineAppConfig } from 'wxt/sandbox';
declare module 'wxt/sandbox' {
export interface WxtAppConfig {
example: string;
}
}
export default defineAppConfig({
example: 'value',
});
@@ -15,6 +15,8 @@ export default defineBackground({
messages,
});
console.log(useAppConfig());
// @ts-expect-error: should only accept entrypoints or public assets
browser.runtime.getURL('/');
browser.runtime.getURL('/background.js');
@@ -13,7 +13,7 @@ export default defineContentScript({
anchor: 'form[role=search]',
onMount: (container) => {
const app = document.createElement('div');
app.textContent = 'Custom content script UI';
app.textContent = browser.i18n.getMessage('prompt_for_name');
container.append(app);
},
});
+2 -1
View File
@@ -2,6 +2,7 @@
"extends": ["../../tsconfig.base.json", "./.wxt/tsconfig.json"],
"compilerOptions": {
"allowImportingTsExtensions": true,
"jsx": "react-jsx"
"jsx": "react-jsx",
"types": ["vitest-plugin-random-seed/types"]
}
}
+4 -1
View File
@@ -6,5 +6,8 @@ export default defineProject({
mockReset: true,
restoreMocks: true,
},
plugins: [WxtVitest()],
plugins: [
// @ts-expect-error: Vite version mismatch
WxtVitest(),
],
});
+5 -1
View File
@@ -2,6 +2,7 @@ import { defineConfig } from 'wxt';
export default defineConfig({
srcDir: 'src',
extensionApi: 'chrome',
manifest: {
permissions: ['storage'],
default_locale: 'en',
@@ -19,7 +20,10 @@ export default defineConfig({
open: true,
},
experimental: {
viteRuntime: true,
entrypointImporter: 'vite-node',
},
runner: {
startUrls: ['https://duckduckgo.com'],
},
example: {
a: 'a',
+155
View File
@@ -1,5 +1,160 @@
# Changelog
## v0.18.15
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.14...wxt-v0.18.15)
### 🩹 Fixes
- Don't throw error if localization file is missing ([#832](https://github.com/wxt-dev/wxt/pull/832))
- Build latest version of package before packing ([88a1244](https://github.com/wxt-dev/wxt/commit/88a1244))
- Externalize app config during dependency optimization ([#833](https://github.com/wxt-dev/wxt/pull/833))
### 📖 Documentation
- Fix links to Guide pages ([#821](https://github.com/wxt-dev/wxt/pull/821))
### ❤️ Contributors
- Eetann ([@eetann](http://github.com/eetann))
## v0.18.14
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.13...wxt-v0.18.14)
### 🩹 Fixes
- **modules:** Add types from all wxt node_modules, not just ones with config ([#817](https://github.com/wxt-dev/wxt/pull/817))
## v0.18.13
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.12...wxt-v0.18.13)
### 🚀 Enhancements
- **config:** `dev.server.hostname` ([#807](https://github.com/wxt-dev/wxt/pull/807))
- Add XPath support to getAnchor() ([#813](https://github.com/wxt-dev/wxt/pull/813))
### 🩹 Fixes
- Add debug logs for vite builder ([#816](https://github.com/wxt-dev/wxt/pull/816))
### ❤️ Contributors
- Ayub Kokabi ([@sir-kokabi](http://github.com/sir-kokabi))
## v0.18.12
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.11...wxt-v0.18.12)
### 🚀 Enhancements
- Support runtime config in `app.config.ts` ([#792](https://github.com/wxt-dev/wxt/pull/792))
### 🔥 Performance
- Create zip using streams ([#793](https://github.com/wxt-dev/wxt/pull/793))
### 🩹 Fixes
- Add missing name to ESLint v9 autoImports config ([#801](https://github.com/wxt-dev/wxt/pull/801))
### 📖 Documentation
- Update README ([#802](https://github.com/wxt-dev/wxt/pull/802))
### 🏡 Chore
- **deps:** Upgrade `web-ext-run` (0.2.0 to 0.2.1) ([#804](https://github.com/wxt-dev/wxt/pull/804))
### ❤️ Contributors
- Ntnyq ([@ntnyq](http://github.com/ntnyq))
- Florian Metz ([@Timeraa](http://github.com/Timeraa))
## v0.18.11
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.10...wxt-v0.18.11)
### 🚀 Enhancements
- Add eslint 9 config support ([#762](https://github.com/wxt-dev/wxt/pull/762))
### 🩹 Fixes
- Respect custom `outDir` when cleaning and zipping ([#774](https://github.com/wxt-dev/wxt/pull/774))
- **dev:** Catch error when attempting to reload a tab in a saved tab group ([#786](https://github.com/wxt-dev/wxt/pull/786))
### 🏡 Chore
- Replace consola with wxt.logger ([#776](https://github.com/wxt-dev/wxt/pull/776))
- **deps:** Upgrade non-major deps ([#778](https://github.com/wxt-dev/wxt/pull/778))
### ❤️ Contributors
- KnightYoshi ([@KnightYoshi](http://github.com/KnightYoshi))
- Asakura Mizu ([@AsakuraMizu](http://github.com/AsakuraMizu))
## v0.18.10
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.9...wxt-v0.18.10)
### 🚀 Enhancements
- Add `prepare:types` hook to extend `.wxt/` directory generation ([#767](https://github.com/wxt-dev/wxt/pull/767))
- **modules:** Allow adding generated public files ([#769](https://github.com/wxt-dev/wxt/pull/769))
### 🩹 Fixes
- Await `prepare:types` hook ([b29d49c](https://github.com/wxt-dev/wxt/commit/b29d49c))
### 🏡 Chore
- Refactor package manager test fixtures ([39f6c29](https://github.com/wxt-dev/wxt/commit/39f6c29))
- Consolidate `unimport` code into a built-in module ([#771](https://github.com/wxt-dev/wxt/pull/771))
## v0.18.9
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.8...wxt-v0.18.9)
### 🚀 Enhancements
- **experimental:** Replace `viteRuntime` option with `entrypointImporter` option, and implement `vite-node` importer ([#761](https://github.com/wxt-dev/wxt/pull/761))
## v0.18.8
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.7...wxt-v0.18.8)
### 🚀 Enhancements
- **dev:** Reload extension when public files change ([#752](https://github.com/wxt-dev/wxt/pull/752))
### 🩹 Fixes
- Don't load plugins twice in HTML pages ([#746](https://github.com/wxt-dev/wxt/pull/746))
- Ignore .wxt file changes in dev ([#755](https://github.com/wxt-dev/wxt/pull/755))
- **modules:** `addViteConfig` ignored user vite config ([#760](https://github.com/wxt-dev/wxt/pull/760))
### 🏡 Chore
- Refactor client web socket util ([#753](https://github.com/wxt-dev/wxt/pull/753))
- Add E2E test for `addImportPreset` ([9fc6408](https://github.com/wxt-dev/wxt/commit/9fc6408))
## v0.18.7
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.6...wxt-v0.18.7)
### 🩹 Fixes
- **dev:** Fix CSP error loading HTML plugins ([#723](https://github.com/wxt-dev/wxt/pull/723))
- Generalize react-refresh preamble logic to virtualize all inline scripts ([#728](https://github.com/wxt-dev/wxt/pull/728))
- **zip:** Revert dotfile changes from #674 ([#742](https://github.com/wxt-dev/wxt/pull/742), [#674](https://github.com/wxt-dev/wxt/issues/674))
### 🏡 Chore
- **deps:** Upgrade and sync all dependencies ([#725](https://github.com/wxt-dev/wxt/pull/725))
- Extract build cache script to NPM package ([#737](https://github.com/wxt-dev/wxt/pull/737))
## v0.18.6
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.18.5...wxt-v0.18.6)
+5 -5
View File
@@ -1,5 +1,5 @@
<h1 align="center">
<img style="vertical-align:middle" width="44" src="./docs/public/hero-logo.svg" alt="WXT Logo">
<img style="vertical-align:middle" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo">
<span>WXT</span>
</h1>
@@ -22,18 +22,18 @@
</p>
<p align="center">
<a href="https://wxt.dev" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/guide/installation.html" target="_blank">Installation</a>
<a href="https://wxt.dev/guide/installation.html" target="_blank">Get Started</a>
&bull;
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
&bull;
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
&bull;
<a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
&bull;
<a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
</p>
![Example CLI Output](./docs/assets/cli-output.png)
![Example CLI Output](https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/assets/cli-output.png)
## Demo
+1 -1
View File
@@ -1,2 +1,2 @@
#!/usr/bin/env node
import '../dist/cli.js';
import '../dist/cli/index.mjs';
+49
View File
@@ -0,0 +1,49 @@
import { defineBuildConfig } from 'unbuild';
import { version } from './package.json';
import { readFile, writeFile } from 'fs/promises';
import {
virtualEntrypointModuleNames,
virtualModuleNames,
} from './src/core/utils/virtual-modules';
export default defineBuildConfig([
// Non-virtual modules can be transpiled with mkdist
{
entries: [
{
builder: 'mkdist',
input: 'src',
pattern: ['**/*', '!**/__tests__', '!**/*.md', '!virtual', '!@types'],
declaration: true,
},
],
hooks: {
async 'build:done'() {
// Replace any template variables in output files
await replaceVars('dist/version.mjs', { version });
},
},
},
// Virtual modules must be bundled individually
...virtualModuleNames.map((moduleName) => ({
entries: [`src/virtual/${moduleName}.ts`],
externals: [
...virtualEntrypointModuleNames.map((name) => `virtual:user-${name}`),
'virtual:wxt-plugins',
'virtual:app-config',
'wxt/browser',
'wxt/sandbox',
'wxt/client',
'wxt/testing',
],
})),
]);
async function replaceVars(file: string, vars: Record<string, string>) {
let text = await readFile(file, 'utf8');
Object.entries(vars).forEach(([name, value]) => {
text = text.replaceAll(`{{${name}}}`, value);
});
await writeFile(file, text, 'utf8');
}
@@ -0,0 +1,111 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Auto Imports > eslintrc > "enabled: 8" should output a JSON config file compatible with ESlint 8 1`] = `
".wxt/eslintrc-auto-import.json
----------------------------------------
{
"globals": {
"ContentScriptContext": true,
"InvalidMatchPattern": true,
"MatchPattern": true,
"browser": true,
"createIframeUi": true,
"createIntegratedUi": true,
"createShadowRootUi": true,
"defineAppConfig": true,
"defineBackground": true,
"defineConfig": true,
"defineContentScript": true,
"defineUnlistedScript": true,
"defineWxtPlugin": true,
"fakeBrowser": true,
"storage": true,
"useAppConfig": true
}
}
"
`;
exports[`Auto Imports > eslintrc > "enabled: 9" should output a flat config file compatible with ESlint 9 1`] = `
".wxt/eslint-auto-imports.mjs
----------------------------------------
const globals = {
"ContentScriptContext": true,
"InvalidMatchPattern": true,
"MatchPattern": true,
"browser": true,
"createIframeUi": true,
"createIntegratedUi": true,
"createShadowRootUi": true,
"defineAppConfig": true,
"defineBackground": true,
"defineConfig": true,
"defineContentScript": true,
"defineUnlistedScript": true,
"defineWxtPlugin": true,
"fakeBrowser": true,
"storage": true,
"useAppConfig": true
}
export default {
name: "wxt/auto-imports",
languageOptions: {
globals,
sourceType: "module",
},
};
"
`;
exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config file compatible with ESlint 8 1`] = `
".wxt/eslintrc-auto-import.json
----------------------------------------
{
"globals": {
"ContentScriptContext": true,
"InvalidMatchPattern": true,
"MatchPattern": true,
"browser": true,
"createIframeUi": true,
"createIntegratedUi": true,
"createShadowRootUi": true,
"defineAppConfig": true,
"defineBackground": true,
"defineConfig": true,
"defineContentScript": true,
"defineUnlistedScript": true,
"defineWxtPlugin": true,
"fakeBrowser": true,
"storage": true,
"useAppConfig": true
}
}
"
`;
exports[`Auto Imports > eslintrc > should allow customizing the output 1`] = `
"example.json
----------------------------------------
{
"globals": {
"ContentScriptContext": "readonly",
"InvalidMatchPattern": "readonly",
"MatchPattern": "readonly",
"browser": "readonly",
"createIframeUi": "readonly",
"createIntegratedUi": "readonly",
"createShadowRootUi": "readonly",
"defineAppConfig": "readonly",
"defineBackground": "readonly",
"defineConfig": "readonly",
"defineContentScript": "readonly",
"defineUnlistedScript": "readonly",
"defineWxtPlugin": "readonly",
"fakeBrowser": "readonly",
"storage": "readonly",
"useAppConfig": "readonly"
}
}
"
`;
+1 -1
View File
@@ -1,6 +1,6 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TestProject } from '../utils';
import { resetBundleIncrement } from '~/core/builders/vite/plugins';
import { resetBundleIncrement } from '../../src/core/builders/vite/plugins';
import open from 'open';
vi.mock('open');
+158 -50
View File
@@ -1,5 +1,6 @@
import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
import { execaCommand } from 'execa';
describe('Auto Imports', () => {
describe('imports: { ... }', () => {
@@ -23,6 +24,7 @@ describe('Auto Imports', () => {
const createIframeUi: typeof import('wxt/client')['createIframeUi']
const createIntegratedUi: typeof import('wxt/client')['createIntegratedUi']
const createShadowRootUi: typeof import('wxt/client')['createShadowRootUi']
const defineAppConfig: typeof import('wxt/sandbox')['defineAppConfig']
const defineBackground: typeof import('wxt/sandbox')['defineBackground']
const defineConfig: typeof import('wxt')['defineConfig']
const defineContentScript: typeof import('wxt/sandbox')['defineContentScript']
@@ -30,6 +32,7 @@ describe('Auto Imports', () => {
const defineWxtPlugin: typeof import('wxt/sandbox')['defineWxtPlugin']
const fakeBrowser: typeof import('wxt/testing')['fakeBrowser']
const storage: typeof import('wxt/storage')['storage']
const useAppConfig: typeof import('wxt/client')['useAppConfig']
}
"
`);
@@ -47,10 +50,10 @@ describe('Auto Imports', () => {
----------------------------------------
// Generated by wxt
/// <reference types="wxt/vite-builder-env" />
/// <reference types="./types/imports.d.ts" />
/// <reference types="./types/paths.d.ts" />
/// <reference types="./types/i18n.d.ts" />
/// <reference types="./types/globals.d.ts" />
/// <reference types="./types/imports.d.ts" />
"
`);
});
@@ -96,7 +99,7 @@ describe('Auto Imports', () => {
});
describe('eslintrc', () => {
it('should output the globals list for ESLint to consume', async () => {
it('"enabled: true" should output a JSON config file compatible with ESlint 8', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html', `<html></html>`);
@@ -108,30 +111,43 @@ describe('Auto Imports', () => {
},
});
expect(await project.serializeFile('.wxt/eslintrc-auto-import.json'))
.toMatchInlineSnapshot(`
".wxt/eslintrc-auto-import.json
----------------------------------------
{
"globals": {
"ContentScriptContext": true,
"InvalidMatchPattern": true,
"MatchPattern": true,
"browser": true,
"createIframeUi": true,
"createIntegratedUi": true,
"createShadowRootUi": true,
"defineBackground": true,
"defineConfig": true,
"defineContentScript": true,
"defineUnlistedScript": true,
"defineWxtPlugin": true,
"fakeBrowser": true,
"storage": true
}
}
"
`);
expect(
await project.serializeFile('.wxt/eslintrc-auto-import.json'),
).toMatchSnapshot();
});
it('"enabled: 8" should output a JSON config file compatible with ESlint 8', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html', `<html></html>`);
await project.prepare({
imports: {
eslintrc: {
enabled: 8,
},
},
});
expect(
await project.serializeFile('.wxt/eslintrc-auto-import.json'),
).toMatchSnapshot();
});
it('"enabled: 9" should output a flat config file compatible with ESlint 9', async () => {
const project = new TestProject();
project.addFile('entrypoints/popup.html', `<html></html>`);
await project.prepare({
imports: {
eslintrc: {
enabled: 9,
},
},
});
expect(
await project.serializeFile('.wxt/eslint-auto-imports.mjs'),
).toMatchSnapshot();
});
it('should allow customizing the output', async () => {
@@ -148,30 +164,122 @@ describe('Auto Imports', () => {
},
});
expect(await project.serializeFile('example.json'))
.toMatchInlineSnapshot(`
"example.json
----------------------------------------
{
"globals": {
"ContentScriptContext": "readonly",
"InvalidMatchPattern": "readonly",
"MatchPattern": "readonly",
"browser": "readonly",
"createIframeUi": "readonly",
"createIntegratedUi": "readonly",
"createShadowRootUi": "readonly",
"defineBackground": "readonly",
"defineConfig": "readonly",
"defineContentScript": "readonly",
"defineUnlistedScript": "readonly",
"defineWxtPlugin": "readonly",
"fakeBrowser": "readonly",
"storage": "readonly"
}
}
"
`);
expect(await project.serializeFile('example.json')).toMatchSnapshot();
});
describe('Actual linting results', () => {
async function runEslint(
project: TestProject,
version: boolean | 'auto' | 8 | 9,
) {
project.addFile(
'entrypoints/background.js',
`export default defineBackground(() => {})`,
);
await project.prepare({
imports: { eslintrc: { enabled: version } },
});
return await execaCommand('pnpm eslint entrypoints/background.js', {
cwd: project.root,
});
}
describe('ESLint 9', () => {
it('should have lint errors when not extending generated config', async () => {
const project = new TestProject({
devDependencies: {
'@eslint/js': '9.5.0',
eslint: '9.5.0',
},
});
project.addFile(
'eslint.config.mjs',
`
import eslint from "@eslint/js";
export default [
eslint.configs.recommended,
];
`,
);
await expect(runEslint(project, 9)).rejects.toMatchObject({
message: expect.stringContaining(
"'defineBackground' is not defined",
),
});
});
it('should not have any lint errors when configured', async () => {
const project = new TestProject({
devDependencies: {
'@eslint/js': '9.5.0',
eslint: '9.5.0',
},
});
project.addFile(
'eslint.config.mjs',
`
import eslint from "@eslint/js";
import autoImports from "./.wxt/eslint-auto-imports.mjs";
export default [
eslint.configs.recommended,
autoImports,
];
`,
);
const res = await runEslint(project, 9);
expect(res).toBeDefined();
});
});
describe('ESLint 8', () => {
it('should have lint errors when not extending generated config', async () => {
const project = new TestProject({
devDependencies: {
eslint: '8.57.0',
},
});
project.addFile(
'.eslintrc',
JSON.stringify({
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
env: { es6: true },
extends: ['eslint:recommended'],
}),
);
await expect(runEslint(project, 8)).rejects.toMatchObject({
message: expect.stringContaining(
"'defineBackground' is not defined",
),
});
});
it('should not have any lint errors when configured', async () => {
const project = new TestProject({
devDependencies: {
eslint: '8.57.0',
},
});
project.addFile(
'.eslintrc',
JSON.stringify({
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
env: { es6: true },
extends: [
'eslint:recommended',
'./.wxt/eslintrc-auto-import.json',
],
}),
);
const res = await runEslint(project, 8);
expect(res).toBeDefined();
});
});
});
});
});
+6 -1
View File
@@ -1,9 +1,10 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { TestProject } from '../utils';
import { WxtHooks } from '~/types';
import { WxtHooks } from '../../src/types';
const hooks: WxtHooks = {
ready: vi.fn(),
'prepare:types': vi.fn(),
'build:before': vi.fn(),
'build:done': vi.fn(),
'build:manifestGenerated': vi.fn(),
@@ -41,6 +42,7 @@ describe('Hooks', () => {
expectHooksToBeCalled({
ready: true,
'prepare:types': true,
'build:before': false,
'build:done': false,
'build:publicAssets': false,
@@ -60,6 +62,7 @@ describe('Hooks', () => {
expectHooksToBeCalled({
ready: true,
'prepare:types': true,
'build:before': true,
'build:done': true,
'build:publicAssets': true,
@@ -79,6 +82,7 @@ describe('Hooks', () => {
expectHooksToBeCalled({
ready: true,
'prepare:types': true,
'build:before': true,
'build:done': true,
'build:publicAssets': true,
@@ -104,6 +108,7 @@ describe('Hooks', () => {
expectHooksToBeCalled({
ready: true,
'prepare:types': true,
'build:before': true,
'build:done': true,
'build:publicAssets': true,
+1 -2
View File
@@ -1,9 +1,8 @@
import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
import { TestProject, WXT_PACKAGE_DIR } from '../utils';
import { execaCommand } from 'execa';
import glob from 'fast-glob';
import { mkdir, writeJson } from 'fs-extra';
import { WXT_PACKAGE_DIR } from '../utils';
describe('Init command', () => {
it('should download and create a template', async () => {
@@ -1,9 +1,9 @@
import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
describe.each([true, false])(
describe.each(['jiti', 'vite-runtime', 'vite-node'] as const)(
'Manifest Content (Vite runtime? %s)',
(viteRuntime) => {
(entrypointImporter) => {
it.each([
{ browser: undefined, outDir: 'chrome-mv3', expected: undefined },
{ browser: 'chrome', outDir: 'chrome-mv3', expected: undefined },
@@ -24,7 +24,7 @@ describe.each([true, false])(
main: () => {},
})`,
);
await project.build({ browser, experimental: { viteRuntime } });
await project.build({ browser, experimental: { entrypointImporter } });
const safariManifest = await project.getOutputManifest(
`.output/${outDir}/manifest.json`,
+43 -18
View File
@@ -2,7 +2,7 @@ import { describe, it, expect, vi } from 'vitest';
import { TestProject } from '../utils';
import type { GenericEntrypoint, InlineConfig } from '../../src/types';
import { readFile } from 'fs-extra';
import { normalizePath } from '~/core/utils/paths';
import { normalizePath } from '../../src/core/utils/paths';
describe('Module Helpers', () => {
describe('options', () => {
@@ -101,7 +101,13 @@ describe('Module Helpers', () => {
export default defineWxtModule((wxt) => {
addPublicAssets(wxt, "${normalizePath(dir)}")
})
wxt.hooks.hook("build:publicAssets", (_, assets) => {
assets.push({
relativeDest: "example/generated.txt",
contents: "",
});
});
});
`,
);
@@ -114,6 +120,9 @@ describe('Module Helpers', () => {
await expect(
project.fileExists('.output/chrome-mv3/module.txt'),
).resolves.toBe(true);
await expect(
project.fileExists('.output/chrome-mv3/example/generated.txt'),
).resolves.toBe(true);
});
it("should not overwrite the user's public files", async () => {
@@ -183,10 +192,8 @@ describe('Module Helpers', () => {
const expectedText = addPluginModule(project);
await project.build({
experimental: {
// reduce build output when comparing test failures
includeBrowserPolyfill: false,
},
// reduce build output when comparing test failures
extensionApi: 'chrome',
});
await expect(project.serializeOutput()).resolves.toContain(expectedText);
@@ -205,10 +212,8 @@ describe('Module Helpers', () => {
const expectedText = addPluginModule(project);
await project.build({
experimental: {
// reduce build output when comparing test failures
includeBrowserPolyfill: false,
},
// reduce build output when comparing test failures
extensionApi: 'chrome',
});
await expect(project.serializeOutput()).resolves.toContain(expectedText);
@@ -228,10 +233,8 @@ describe('Module Helpers', () => {
const expectedText = addPluginModule(project);
await project.build({
experimental: {
// reduce build output when comparing test failures
includeBrowserPolyfill: false,
},
// reduce build output when comparing test failures
extensionApi: 'chrome',
});
await expect(project.serializeOutput()).resolves.toContain(expectedText);
@@ -246,10 +249,8 @@ describe('Module Helpers', () => {
const expectedText = addPluginModule(project);
await project.build({
experimental: {
// reduce build output when comparing test failures
includeBrowserPolyfill: false,
},
// reduce build output when comparing test failures
extensionApi: 'chrome',
});
await expect(project.serializeOutput()).resolves.toContain(expectedText);
@@ -287,5 +288,29 @@ describe('Module Helpers', () => {
await expect(project.serializeOutput()).resolves.toContain(expectedText);
});
it('should add preset', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/background.ts',
`export default defineBackground(() => {
customImport();
});`,
);
project.addFile(
'modules/test.ts',
`import { defineWxtModule, addImportPreset } from 'wxt/modules';
export default defineWxtModule((wxt) => {
addImportPreset(wxt, "vue");
})`,
);
await project.build();
await expect(
project.serializeFile('.wxt/types/imports.d.ts'),
).resolves.toContain("const ref: typeof import('vue')['ref']");
});
});
});
+24 -16
View File
@@ -262,10 +262,9 @@ describe('Output Directory Structure', () => {
project.addFile('entrypoints/popup/main.ts', `logHello('popup')`);
await project.build({
experimental: {
// Simplify the build output for comparison
includeBrowserPolyfill: false,
},
// Simplify the build output for comparison
extensionApi: 'chrome',
vite: () => ({
build: {
// Make output for snapshot readible
@@ -278,6 +277,7 @@ describe('Output Directory Structure', () => {
.toMatchInlineSnapshot(`
".output/chrome-mv3/background.js
----------------------------------------
var _a, _b;
import { l as logHello, i as initPlugins } from "./chunks/_virtual_wxt-plugins-OjKtWpmY.js";
function defineBackground(arg) {
if (typeof arg === "function") return { main: arg };
@@ -289,17 +289,21 @@ describe('Output Directory Structure', () => {
logHello("background");
}
});
chrome;
// @ts-expect-error
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) == null ? globalThis.chrome : (
// @ts-expect-error
globalThis.browser
);
function print(method, ...args) {
return;
}
var logger = {
const logger = {
debug: (...args) => print(console.debug, ...args),
log: (...args) => print(console.log, ...args),
warn: (...args) => print(console.warn, ...args),
error: (...args) => print(console.error, ...args)
};
var result;
let result;
try {
initPlugins();
result = definition.main();
@@ -343,10 +347,9 @@ describe('Output Directory Structure', () => {
project.addFile('entrypoints/popup/main.ts', `logHello('popup')`);
await project.build({
experimental: {
// Simplify the build output for comparison
includeBrowserPolyfill: false,
},
// Simplify the build output for comparison
extensionApi: 'chrome',
vite: () => ({
build: {
// Make output for snapshot readible
@@ -361,6 +364,7 @@ describe('Output Directory Structure', () => {
----------------------------------------
var _background = function() {
"use strict";
var _a, _b;
function defineBackground(arg) {
if (typeof arg === "function") return { main: arg };
return arg;
@@ -377,17 +381,21 @@ describe('Output Directory Structure', () => {
_background;
function initPlugins() {
}
chrome;
// @ts-expect-error
((_b = (_a = globalThis.browser) == null ? void 0 : _a.runtime) == null ? void 0 : _b.id) == null ? globalThis.chrome : (
// @ts-expect-error
globalThis.browser
);
function print(method, ...args) {
return;
}
var logger = {
const logger = {
debug: (...args) => print(console.debug, ...args),
log: (...args) => print(console.log, ...args),
warn: (...args) => print(console.warn, ...args),
error: (...args) => print(console.error, ...args)
};
var result;
let result;
try {
initPlugins();
result = definition.main();
@@ -400,8 +408,8 @@ describe('Output Directory Structure', () => {
logger.error("The background crashed on startup!");
throw err;
}
var background_entrypoint_default = result;
return background_entrypoint_default;
const result$1 = result;
return result$1;
}();
_background;
"
@@ -53,7 +53,7 @@ describe('TypeScript Project', () => {
| "/popup.html"
| "/sandbox.html"
type HtmlPublicPath = Extract<PublicPath, \`\${string}.html\`>
export interface WxtRuntime extends Runtime.Static {
export interface WxtRuntime {
getURL(path: PublicPath): string;
getURL(path: \`\${HtmlPublicPath}\${string}\`): string;
}
@@ -233,10 +233,10 @@ describe('TypeScript Project', () => {
----------------------------------------
// Generated by wxt
/// <reference types="wxt/vite-builder-env" />
/// <reference types="./types/imports.d.ts" />
/// <reference types="./types/paths.d.ts" />
/// <reference types="./types/i18n.d.ts" />
/// <reference types="./types/globals.d.ts" />
/// <reference types="./types/imports.d.ts" />
"
`);
});
+3 -5
View File
@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest';
import { TestProject } from '../utils';
import { InlineConfig } from '~/types';
import { InlineConfig } from '../../src/types';
describe('User Config', () => {
// Root directory is tested with all tests.
@@ -88,7 +88,7 @@ describe('User Config', () => {
`);
});
it('should exclude the polyfill when the experimental setting is set to false', async () => {
it('should exclude the polyfill when extensionApi="chrome"', async () => {
const buildBackground = async (config?: InlineConfig) => {
const background = `export default defineBackground(() => console.log(browser.runtime.id));`;
const projectWithPolyfill = new TestProject();
@@ -101,9 +101,7 @@ describe('User Config', () => {
const withPolyfill = await buildBackground();
const withoutPolyfill = await buildBackground({
experimental: {
includeBrowserPolyfill: false,
},
extensionApi: 'chrome',
});
expect(withoutPolyfill).not.toBe(withPolyfill);
});
+4 -2
View File
@@ -97,7 +97,8 @@ describe('Zipping', () => {
expect(await project.fileExists(sourcesZip)).toBe(true);
});
it('should not zip hidden files into sources by default', async () => {
// TODO: Fix #738 and re-enable
it.skip('should not zip hidden files into sources by default', async () => {
const project = new TestProject({
name: 'test',
version: '1.0.0',
@@ -117,7 +118,8 @@ describe('Zipping', () => {
expect(await project.fileExists(unzipDir, '.env')).toBe(false);
});
it('should allow zipping hidden files into sources when explicitly listed', async () => {
// TODO: Fix #738 and re-enable
it.skip('should allow zipping hidden files into sources when explicitly listed', async () => {
const project = new TestProject({
name: 'test',
version: '1.0.0',
+60 -64
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.18.6",
"version": "0.19.0-alpha1",
"description": "Next gen framework for developing web extensions",
"repository": {
"type": "git",
@@ -30,75 +30,59 @@
"wxt": "./bin/wxt.mjs",
"wxt-publish-extension": "./bin/wxt-publish-extension.cjs"
},
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
},
"./client": {
"types": "./dist/client.d.ts",
"import": "./dist/client.js"
"types": "./dist/client/index.d.ts",
"default": "./dist/client/index.mjs"
},
"./sandbox": {
"types": "./dist/sandbox.d.ts",
"import": "./dist/sandbox.js"
"types": "./dist/sandbox/index.d.ts",
"default": "./dist/sandbox/index.mjs"
},
"./browser": {
"types": "./dist/browser.d.ts",
"import": "./dist/browser.js"
"types": "./dist/browser/index.d.ts",
"default": "./dist/browser/index.mjs"
},
"./browser/chrome": {
"types": "./dist/browser/chrome.d.ts",
"import": "./dist/browser/chrome.mjs"
},
"./browser/webextension-polyfill": {
"types": "./dist/browser/webextension-polyfill.d.ts",
"import": "./dist/browser/webextension-polyfill.mjs"
},
"./testing": {
"import": {
"types": "./dist/testing.d.ts",
"default": "./dist/testing.js"
},
"require": {
"types": "./dist/testing.d.cts",
"default": "./dist/testing.cjs"
}
"types": "./dist/testing/index.d.ts",
"default": "./dist/testing/index.mjs"
},
"./storage": {
"import": {
"types": "./dist/storage.d.ts",
"default": "./dist/storage.js"
},
"require": {
"types": "./dist/storage.d.cts",
"default": "./dist/storage.cjs"
}
"types": "./dist/storage.d.ts",
"default": "./dist/storage.mjs"
},
"./vite-builder-env": {
"types": "./dist/vite-builder-env.d.ts"
},
"./modules": {
"import": {
"types": "./dist/modules.d.ts",
"default": "./dist/modules.js"
},
"require": {
"types": "./dist/modules.d.cts",
"default": "./dist/modules.cjs"
}
"types": "./dist/modules.d.ts",
"default": "./dist/modules.mjs"
}
},
"scripts": {
"wxt": "tsx src/cli/index.ts",
"build": "tsx scripts/build.ts",
"check": "run-s -c check:*",
"build": "buildc -- unbuild",
"check": "buildc --deps-only -- run-s -c check:*",
"check:default": "check",
"check:tsc-virtual": "tsc --noEmit -p src/virtual",
"test": "vitest",
"test:e2e": "vitest -r e2e",
"sync-releases": "pnpx changelogen@latest gh release"
"test": "buildc --deps-only -- vitest",
"test:coverage": "pnpm test -- run --coverage",
"sync-releases": "pnpx changelogen@latest gh release",
"prepack": "pnpm build"
},
"dependencies": {
"@aklinker1/rollup-plugin-visualizer": "5.12.0",
@@ -106,16 +90,16 @@
"@webext-core/fake-browser": "^1.3.1",
"@webext-core/isolated-element": "^1.1.2",
"@webext-core/match-patterns": "^1.0.3",
"async-mutex": "^0.4.1",
"async-mutex": "^0.5.0",
"c12": "^1.11.1",
"cac": "^6.7.14",
"chokidar": "^3.6.0",
"ci-info": "^4.0.0",
"defu": "^6.1.4",
"dequal": "^2.0.3",
"esbuild": "^0.19.12",
"esbuild": "^0.23.0",
"fast-glob": "^3.3.2",
"filesize": "^10.1.2",
"filesize": "^10.1.4",
"fs-extra": "^11.2.0",
"get-port": "^7.1.0",
"giget": "^1.2.3",
@@ -124,40 +108,52 @@
"jiti": "^1.21.6",
"json5": "^2.2.3",
"jszip": "^3.10.1",
"linkedom": "^0.18.3",
"linkedom": "^0.18.4",
"magicast": "^0.3.4",
"minimatch": "^9.0.4",
"minimatch": "^10.0.1",
"natural-compare": "^1.4.0",
"normalize-path": "^3.0.0",
"nypm": "^0.3.8",
"nypm": "^0.3.9",
"ohash": "^1.1.3",
"open": "^10.1.0",
"ora": "^7.0.1",
"ora": "^8.0.1",
"picocolors": "^1.0.1",
"prompts": "^2.4.2",
"publish-browser-extension": "^2.1.3",
"unimport": "^3.7.2",
"vite": "^5.3.0",
"web-ext-run": "^0.2.0",
"webextension-polyfill": "^0.10.0"
"unimport": "^3.9.0",
"vite": "^5.3.4",
"vite-node": "^2.0.0",
"web-ext-run": "^0.2.1",
"webextension-polyfill": "^0.12.0"
},
"devDependencies": {
"@aklinker1/check": "^1.3.1",
"@faker-js/faker": "^8.4.1",
"@types/chrome": "^0.0.268",
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
"@types/natural-compare": "^1.4.3",
"@types/node": "^20.14.2",
"@types/node": "^20.14.11",
"@types/normalize-path": "^3.0.2",
"@types/prompts": "^2.4.9",
"execa": "^9.2.0",
"execa": "^9.3.0",
"extract-zip": "^2.0.1",
"happy-dom": "^13.10.1",
"happy-dom": "^14.12.3",
"lodash.merge": "^4.6.2",
"p-map": "^7.0.2",
"publint": "^0.2.8",
"tsup": "^8.1.0",
"tsx": "^4.15.4",
"typescript": "^5.4.5"
"publint": "^0.2.9",
"tsx": "4.15.7",
"typescript": "^5.5.3",
"unbuild": "^2.0.0",
"vitest": "^2.0.3",
"vitest-plugin-random-seed": "^1.1.0"
},
"peerDependencies": {
"@types/chrome": "*"
},
"peerDependenciesMeta": {
"@types/chrome": {
"optional": true
}
}
}
-125
View File
@@ -1,125 +0,0 @@
import tsup from 'tsup';
import glob from 'fast-glob';
import { printFileList } from '~/core/utils/log';
import { formatDuration } from '~/core/utils/time';
import ora from 'ora';
import fs from 'fs-extra';
import { consola } from 'consola';
import pMap from 'p-map';
import os from 'node:os';
import path from 'node:path';
import {
virtualModuleNames,
virtualEntrypointModuleNames,
} from '~/core/utils/virtual-modules';
const spinnerText = 'Building WXT';
const spinner = ora(spinnerText).start();
const startTime = Date.now();
const outDir = 'dist';
await fs.rm(path.join(outDir, '*'), { recursive: true, force: true });
const preset = {
dts: true,
silent: true,
sourcemap: false,
external: [
...virtualEntrypointModuleNames.map((name) => `virtual:user-${name}`),
'virtual:wxt-plugins',
],
} satisfies tsup.Options;
function spinnerPMap(configs: tsup.Options[]) {
let progress = 1;
const updateSpinner = () => {
spinner.text = `${spinnerText} [${progress}/${configs.length}]`;
};
updateSpinner();
return pMap(
config,
async (config) => {
const res = await tsup.build(config);
progress++;
updateSpinner();
return res;
},
{
stopOnError: true,
concurrency: process.env.CI === 'true' ? os.cpus().length : Infinity,
},
);
}
const config: tsup.Options[] = [
// CJS/ESM
{
...preset,
entry: {
index: 'src/index.ts',
testing: 'src/testing/index.ts',
storage: 'src/storage.ts',
modules: 'src/modules.ts',
},
format: ['cjs', 'esm'],
clean: true,
},
// ESM-only
{
...preset,
entry: {
browser: 'src/browser.ts',
sandbox: 'src/sandbox/index.ts',
client: 'src/client/index.ts',
},
format: ['esm'],
},
{
...preset,
entry: virtualModuleNames.reduce<Record<string, string>>(
(acc, moduleName) => {
acc[`virtual/${moduleName}`] = `src/virtual/${moduleName}.ts`;
return acc;
},
{},
),
format: ['esm'],
splitting: false,
dts: false,
external: [...preset.external, 'wxt'],
},
// CJS-only
{
...preset,
entry: {
cli: 'src/cli/index.ts',
},
format: ['esm'],
banner: {
// Fixes dynamic require of nodejs modules. See https://github.com/wxt-dev/wxt/issues/355
// https://github.com/evanw/esbuild/issues/1921#issuecomment-1152991694
js: "import { createRequire } from 'module';const require = createRequire(import.meta.url);",
},
},
];
await spinnerPMap(config).catch((err) => {
spinner.fail();
console.error(err);
process.exit(1);
});
// Copy "public" files that need shipped inside WXT
await fs.copyFile('src/vite-builder-env.d.ts', 'dist/vite-builder-env.d.ts');
spinner.clear().stop();
const duration = Date.now() - startTime;
const outFiles = await glob(`${outDir}/**`, { absolute: true });
await printFileList(
consola.success,
`Built WXT in ${formatDuration(duration)}`,
outDir,
outFiles,
);
@@ -49,8 +49,3 @@ declare module 'web-ext-run/util/logger' {
}
export const consoleStream: IConsoleStream;
}
declare module 'wxt/browser' {
// Overridden when types are generated per project
export type PublicPath = string;
}
+4 -4
View File
@@ -1,4 +1,4 @@
import { fakeWxt } from '~/core/utils/testing/fake-objects';
import { fakeWxt } from '../core/utils/testing/fake-objects';
import { addImportPreset, addViteConfig } from '../modules';
import { describe, it, expect } from 'vitest';
import { createHooks } from 'hookable';
@@ -13,10 +13,10 @@ describe('Module Utilities', () => {
const userConfig = {};
const moduleConfig = { build: { sourcemap: true } };
wxt.config.vite = () => userConfig;
wxt.config.vite = () => Promise.resolve(userConfig);
addViteConfig(wxt, () => moduleConfig);
await wxt.hooks.callHook('ready', wxt);
const actual: any = wxt.config.vite(wxt.config.env);
const actual = await wxt.config.vite(wxt.config.env);
expect(actual).toEqual(expected);
});
@@ -32,7 +32,7 @@ describe('Module Utilities', () => {
wxt.config.vite = () => userConfig;
addViteConfig(wxt, () => moduleConfig);
await wxt.hooks.callHook('ready', wxt);
const actual: any = wxt.config.vite(wxt.config.env);
const actual = await wxt.config.vite(wxt.config.env);
expect(actual).toEqual(expected);
});
+2 -2
View File
@@ -1,7 +1,7 @@
import { fakeBrowser } from '@webext-core/fake-browser';
import { describe, it, expect, beforeEach, vi, expectTypeOf } from 'vitest';
import { browser } from '~/browser';
import { WxtStorageItem, storage } from '~/storage';
import { browser } from 'wxt/browser';
import { WxtStorageItem, storage } from '../storage';
/**
* This works because fakeBrowser is synchronous, and is will finish any number of chained
+28
View File
@@ -0,0 +1,28 @@
/// <reference types="chrome" />
/**
* EXPERIMENTAL
*
* Includes the `chrome` API and types when using `extensionApi: 'chrome'`.
*
* @module wxt/browser/chrome
*/
export interface WxtRuntime {
// Overriden per-project
}
export interface WxtI18n {
// Overriden per-project
}
export type Chrome = typeof chrome;
export type WxtBrowser = Omit<Chrome, 'runtime' | 'i18n'> & {
runtime: WxtRuntime & Omit<Chrome['runtime'], 'getURL'>;
i18n: WxtI18n & Omit<Chrome['i18n'], 'getMessage'>;
};
export const browser: WxtBrowser =
// @ts-expect-error
globalThis.browser?.runtime?.id == null
? globalThis.chrome
: // @ts-expect-error
globalThis.browser;
+4
View File
@@ -0,0 +1,4 @@
/**
* @module wxt/browser
*/
export * from './webextension-polyfill';
@@ -1,6 +1,9 @@
/**
* @module wxt/browser
* Includes the `browser` API and types when using `extensionApi: 'webextension-polyfill'` (the default).
*
* @module wxt/browser/webextension-polyfill
*/
import originalBrowser, { Browser, Runtime, I18n } from 'webextension-polyfill';
export interface AugmentedBrowser extends Browser {
@@ -0,0 +1,4 @@
import { WxtModule } from '../types';
import unimport from './unimport';
export const builtinModules: WxtModule<any>[] = [unimport];
@@ -0,0 +1,150 @@
import { addViteConfig, defineWxtModule } from '../modules';
import type {
EslintGlobalsPropValue,
WxtDirFileEntry,
WxtModule,
WxtResolvedUnimportOptions,
} from '../types';
import { type Unimport, createUnimport } from 'unimport';
import { Plugin } from 'vite';
import { extname } from 'node:path';
export default defineWxtModule({
name: 'wxt:built-in:unimport',
setup(wxt) {
const options = wxt.config.imports;
if (options === false) return;
let unimport: Unimport;
// Add user module imports to config
wxt.hooks.hook('ready', () => {
const addModuleImports = (module: WxtModule<any>) => {
if (!module.imports) return;
options.imports ??= [];
options.imports.push(...module.imports);
};
wxt.config.builtinModules.forEach(addModuleImports);
wxt.config.userModules.forEach(addModuleImports);
});
// Create unimport instance AFTER "ready" so any modifications to the
// config inside "ready" are applied.
wxt.hooks.afterEach((event) => {
if (event.name === 'ready') {
unimport = createUnimport(options);
}
});
// Generate types
wxt.hooks.hook('prepare:types', async (_, entries) => {
// Update cache before each rebuild
await unimport.init();
entries.push(await getImportsDeclarationEntry(unimport));
if (options.eslintrc.enabled === false) return;
entries.push(
await getEslintConfigEntry(unimport, options.eslintrc.enabled, options),
);
});
// Add vite plugin
addViteConfig(wxt, () => ({
plugins: [vitePlugin(unimport)],
}));
},
});
export function vitePlugin(unimport: Unimport): Plugin {
const ENABLED_EXTENSIONS = new Set([
'.js',
'.jsx',
'.ts',
'.tsx',
'.vue',
'.svelte',
]);
return {
name: 'wxt:unimport',
async transform(code, id) {
// Don't transform dependencies
if (id.includes('node_modules')) return;
// Don't transform non-js files
if (!ENABLED_EXTENSIONS.has(extname(id))) return;
const injected = await unimport.injectImports(code, id);
return {
code: injected.code,
map: injected.s.generateMap({ hires: 'boundary', source: id }),
};
},
};
}
async function getImportsDeclarationEntry(
unimport: Unimport,
): Promise<WxtDirFileEntry> {
// Load project imports into unimport memory so they are output via generateTypeDeclarations
await unimport.init();
return {
path: 'types/imports.d.ts',
text: [
'// Generated by wxt',
await unimport.generateTypeDeclarations(),
'',
].join('\n'),
tsReference: true,
};
}
async function getEslintConfigEntry(
unimport: Unimport,
version: 8 | 9,
options: WxtResolvedUnimportOptions,
): Promise<WxtDirFileEntry> {
const globals = (await unimport.getImports())
.map((i) => i.as ?? i.name)
.filter(Boolean)
.sort()
.reduce<Record<string, EslintGlobalsPropValue>>((globals, name) => {
globals[name] = options.eslintrc.globalsPropValue;
return globals;
}, {});
if (version <= 8) return getEslint8ConfigEntry(options, globals);
else return getEslint9ConfigEntry(options, globals);
}
export function getEslint8ConfigEntry(
options: WxtResolvedUnimportOptions,
globals: Record<string, EslintGlobalsPropValue>,
): WxtDirFileEntry {
return {
path: options.eslintrc.filePath,
text: JSON.stringify({ globals }, null, 2) + '\n',
};
}
export function getEslint9ConfigEntry(
options: WxtResolvedUnimportOptions,
globals: Record<string, EslintGlobalsPropValue>,
): WxtDirFileEntry {
return {
path: options.eslintrc.filePath,
text: `const globals = ${JSON.stringify(globals, null, 2)}
export default {
name: "wxt/auto-imports",
languageOptions: {
globals,
sourceType: "module",
},
};
`,
};
}
+33 -15
View File
@@ -1,29 +1,29 @@
import { describe, it, vi, beforeEach, expect } from 'vitest';
import { build } from '~/core/build';
import { createServer } from '~/core/create-server';
import { zip } from '~/core/zip';
import { prepare } from '~/core/prepare';
import { clean } from '~/core/clean';
import { initialize } from '~/core/initialize';
import { build } from '../../core/build';
import { createServer } from '../../core/create-server';
import { zip } from '../../core/zip';
import { prepare } from '../../core/prepare';
import { clean } from '../../core/clean';
import { initialize } from '../../core/initialize';
import { mock } from 'vitest-mock-extended';
import consola from 'consola';
vi.mock('~/core/build');
vi.mock('../../core/build');
const buildMock = vi.mocked(build);
vi.mock('~/core/create-server');
vi.mock('../../core/create-server');
const createServerMock = vi.mocked(createServer);
vi.mock('~/core/zip');
vi.mock('../../core/zip');
const zipMock = vi.mocked(zip);
vi.mock('~/core/prepare');
vi.mock('../../core/prepare');
const prepareMock = vi.mocked(prepare);
vi.mock('~/core/clean');
vi.mock('../../core/clean');
const cleanMock = vi.mocked(clean);
vi.mock('~/core/initialize');
vi.mock('../../core/initialize');
const initializeMock = vi.mocked(initialize);
consola.wrapConsole();
@@ -35,7 +35,7 @@ function mockArgv(...args: string[]) {
}
async function importCli() {
await import('~/cli');
await import('../../cli');
}
describe('CLI', () => {
@@ -345,14 +345,32 @@ describe('CLI', () => {
mockArgv('clean');
await importCli();
expect(cleanMock).toBeCalledWith(undefined);
expect(cleanMock).toBeCalledWith({});
});
it('should respect passing a custom root', async () => {
mockArgv('clean', 'path/to/root');
await importCli();
expect(cleanMock).toBeCalledWith('path/to/root');
expect(cleanMock).toBeCalledWith({ root: 'path/to/root' });
});
it('should respect a custom config file', async () => {
mockArgv('clean', '-c', './path/to/config.ts');
await importCli();
expect(cleanMock).toBeCalledWith({
configFile: './path/to/config.ts',
});
});
it('should respect passing --debug', async () => {
mockArgv('clean', '--debug');
await importCli();
expect(cleanMock).toBeCalledWith({
debug: true,
});
});
});
+5 -5
View File
@@ -1,10 +1,10 @@
import { CAC, Command } from 'cac';
import consola, { LogLevels } from 'consola';
import { filterTruthy, toArray } from '~/core/utils/arrays';
import { printHeader } from '~/core/utils/log';
import { formatDuration } from '~/core/utils/time';
import { ValidationError } from '~/core/utils/validation';
import { registerWxt } from '~/core/wxt';
import { filterTruthy, toArray } from '../core/utils/arrays';
import { printHeader } from '../core/utils/log';
import { formatDuration } from '../core/utils/time';
import { ValidationError } from '../core/utils/validation';
import { registerWxt } from '../core/wxt';
/**
* Wrap an action handler to add a timer, error handling, and maybe enable debug mode.
+3 -2
View File
@@ -1,5 +1,5 @@
import cac from 'cac';
import { build, clean, createServer, initialize, prepare, zip } from '~/core';
import { build, clean, createServer, initialize, prepare, zip } from '../core';
import {
createAliasedCommand,
getArrayFromFlags,
@@ -126,9 +126,10 @@ cli
cli
.command('clean [root]', 'clean generated files and caches')
.alias('cleanup')
.option('-c, --config <file>', 'use specified config file')
.action(
wrapAction(async (root, flags) => {
await clean(root);
await clean({ root, configFile: flags.config, debug: flags.debug });
}),
);
+1 -1
View File
@@ -1,5 +1,5 @@
import cli from './commands';
import { version } from '~/version';
import { version } from '../version';
import { isAliasedCommand } from './cli-utils';
// TODO: Remove. See https://github.com/wxt-dev/wxt/issues/277
+7
View File
@@ -0,0 +1,7 @@
// @ts-expect-error: Untyped virtual module
import appConfig from 'virtual:app-config';
import type { WxtAppConfig } from '../sandbox/define-app-config';
export function useAppConfig(): WxtAppConfig {
return appConfig;
}
@@ -1,6 +1,6 @@
import { ContentScriptDefinition } from '~/types';
import { browser } from '~/browser';
import { logger } from '~/sandbox/utils/logger';
import { ContentScriptDefinition } from '../../types';
import { browser } from 'wxt/browser';
import { logger } from '../../sandbox/utils/logger';
import { WxtLocationChangeEvent, getUniqueEventName } from './custom-events';
import { createLocationWatcher } from './location-watcher';
@@ -1,4 +1,4 @@
import { browser } from '~/browser';
import { browser } from 'wxt/browser';
export class WxtLocationChangeEvent extends Event {
static EVENT_NAME = getUniqueEventName('wxt:locationchange');
@@ -22,5 +22,5 @@ export function getUniqueEventName(eventName: string): string {
? 'build'
: import.meta.env.ENTRYPOINT;
return `${browser.runtime.id}:${entrypointName}:${eventName}`;
return `${browser?.runtime?.id}:${entrypointName}:${eventName}`;
}
@@ -252,6 +252,24 @@ describe('Content Script UIs', () => {
document.querySelector('#parent > div[data-wxt-integrated]'),
).not.toBeNull();
});
it('should append the element using an XPath string', () => {
vi.stubGlobal('XPathResult', { FIRST_ORDERED_NODE_TYPE: 9 });
document.evaluate = vi.fn().mockReturnValue({
singleNodeValue: document.querySelector('#three'),
});
const ui = createIntegratedUi(ctx, {
position: 'inline',
onMount: appendTestApp,
anchor: '//p[@id="three"]',
});
ui.mount();
expect(
document.querySelector('#three > div[data-wxt-integrated]'),
).not.toBeNull();
});
});
describe('Element', () => {
@@ -1,4 +1,4 @@
import { browser } from '~/browser';
import { browser } from 'wxt/browser';
import { ContentScriptContext } from '..';
import {
ContentScriptAnchoredOptions,
@@ -10,7 +10,7 @@ import {
ShadowRootContentScriptUi,
ShadowRootContentScriptUiOptions,
} from './types';
import { logger } from '~/sandbox/utils/logger';
import { logger } from '../../../sandbox/utils/logger';
import { createIsolatedElement } from '@webext-core/isolated-element';
export * from './types';
@@ -199,8 +199,25 @@ function getAnchor(options: ContentScriptAnchoredOptions): Element | undefined {
let resolved =
typeof options.anchor === 'function' ? options.anchor() : options.anchor;
if (typeof resolved === 'string')
return document.querySelector<Element>(resolved) ?? undefined;
if (typeof resolved === 'string') {
// If the string is an XPath expression (starts with '//' or '/')
if (resolved.startsWith('/')) {
// Evaluate the XPath and return the first ordered node
const result = document.evaluate(
resolved,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null,
);
return (result.singleNodeValue as Element) ?? undefined;
} else {
// If the string is a CSS selector, query the document and return the element
return document.querySelector<Element>(resolved) ?? undefined;
}
}
return resolved ?? undefined;
}
@@ -194,7 +194,7 @@ export type ContentScriptPositioningOptions =
export interface ContentScriptAnchoredOptions {
/**
* A CSS selector, element, or function that returns one of the two. Along with `append`, the
* A CSS selector, XPath expression, element, or function that returns one of the three. Along with `append`, the
* `anchor` dictates where in the page the UI will be added.
*/
anchor?:
+1
View File
@@ -4,3 +4,4 @@
* @module wxt/client
*/
export * from './content-scripts';
export * from './app-config';
+1 -1
View File
@@ -1,4 +1,4 @@
import { BuildOutput, InlineConfig } from '~/types';
import { BuildOutput, InlineConfig } from '../types';
import { internalBuild } from './utils/building';
import { registerWxt } from './wxt';
@@ -1,22 +0,0 @@
import { describe, it, expect } from 'vitest';
import { createViteBuilder } from '../index';
import { fakeResolvedConfig } from '~/core/utils/testing/fake-objects';
import { createHooks } from 'hookable';
describe('Vite Builder', () => {
describe('importEntrypoint', () => {
it('should import entrypoints, removing runtime values (like the main function)', async () => {
const {
default: { main: _, ...expected },
} = await import('./fixtures/module');
const builder = await createViteBuilder(
fakeResolvedConfig({ root: __dirname }),
createHooks(),
);
const actual = await builder.importEntrypoint<{ default: any }>(
'./fixtures/module.ts',
);
expect(actual).toEqual(expected);
});
});
});

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