Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 229dfc822d | |||
| 7177405c11 | |||
| 24e4abc087 | |||
| ac9a4380b1 | |||
| 71620a02ab | |||
| 0aca961629 | |||
| d64af5d4f0 | |||
| 7eb0a36245 | |||
| 14bae66bba | |||
| e4ec4a28f9 | |||
| 7b32dd7062 | |||
| b1ef71a77b | |||
| 2f7fb30467 | |||
| 4c684e7966 | |||
| 8d5fbb2a32 | |||
| 5eb5be7123 | |||
| 886b2081c2 | |||
| c69e51ce50 | |||
| 861efa3415 | |||
| 792b57542d | |||
| 97e6678e5b | |||
| 66c4ec3f99 | |||
| 8988d6a241 | |||
| b52856bea4 | |||
| 16fee19b85 | |||
| 14011c19cb | |||
| 005a9dff16 | |||
| 3109bbadbe | |||
| 34ae689569 | |||
| 1f216dd57b | |||
| a7963e23db | |||
| 069bd6c435 | |||
| 88a1244ca9 | |||
| 7ee391f688 | |||
| 2b3eab9c2a | |||
| a3fc9e1e18 | |||
| 559d32e6eb | |||
| f904b79367 | |||
| 9cd7cca81c | |||
| 3366c1e35f | |||
| 82be31a408 | |||
| d767f3f598 | |||
| 48967c7c5f | |||
| 6c4da7225d | |||
| e9025fb13e | |||
| 8db6b3187b | |||
| a19f9e12c4 | |||
| a82eb46511 | |||
| e50f2b047a | |||
| 1b41cd26ee | |||
| e32fbed1b7 | |||
| f8acdd6352 | |||
| 9ea89c6340 | |||
| a521a80d43 | |||
| 4c6a57dd33 | |||
| 3ce8c8b1f4 | |||
| 4ee8f7837e | |||
| 1e8e5488af | |||
| 5704765cc4 | |||
| 8c7adbf463 | |||
| 8c4436efe8 | |||
| 5e361c0085 | |||
| ccfc207e92 | |||
| 36be4ce771 | |||
| 18f8a387b0 | |||
| 48c2c07557 | |||
| 61548a9478 | |||
| fd678e9572 | |||
| 63fb913f76 | |||
| faffe20a62 | |||
| 3ed8bd10fd | |||
| 6f3f270573 | |||
| 5347cb4626 | |||
| d6b4d2d1f6 | |||
| 0d7ae287da | |||
| 90cb3bd3bf | |||
| 60eb97c4b2 | |||
| 0ffc6eae3e |
@@ -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
|
||||
|
||||
@@ -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>
|
||||
•
|
||||
<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>
|
||||
•
|
||||
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
|
||||
•
|
||||
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
|
||||
•
|
||||
<a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
|
||||
•
|
||||
<a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
|
||||
</p>
|
||||
|
||||

|
||||

|
||||
|
||||
## 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 →</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>
|
||||
@@ -30,6 +30,11 @@ const chromeExtensionIds = [
|
||||
'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);
|
||||
|
||||
@@ -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,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);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
|
||||
- ...
|
||||
|
||||
@@ -113,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
|
||||
|
||||
@@ -25,7 +25,7 @@ In general, you'll need to:
|
||||
 <input type="checkbox" /> Convert custom import syntax to be compatible with Vite<br />
|
||||
 <input type="checkbox" /> Add a default export to JS entrypoints<br />
|
||||
 <input type="checkbox" /> Use the `browser` global instead of `chrome`<br />
|
||||
 <input type="checkbox" /> Compare final `manifest.json` files, making sure permissions and host permissions are unchanged<br />
|
||||
 <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 />
|
||||
 <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!
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)!
|
||||
:::
|
||||
|
||||
@@ -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'],
|
||||
};
|
||||
```
|
||||
|
||||
@@ -95,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
|
||||
|
||||
@@ -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 { ... }
|
||||
}
|
||||
})
|
||||
|
||||
+12
-14
@@ -5,11 +5,11 @@
|
||||
"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",
|
||||
@@ -18,31 +18,29 @@
|
||||
"docs:preview": "pnpm -s docs:gen && vitepress preview docs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/buildc": "^1.0.7",
|
||||
"@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"
|
||||
},
|
||||
|
||||
@@ -1,5 +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 🎉
|
||||
Initial release 🎉
|
||||
@@ -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,
|
||||
});
|
||||
@@ -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}',
|
||||
];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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,8 +21,10 @@
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
"check": "buildc --deps-only -- check",
|
||||
"build": "buildc -- unbuild",
|
||||
"check": "buildc --deps-only -- check"
|
||||
"prepare": "buildc --deps-only -- wxt prepare"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"wxt": ">=0.18.6"
|
||||
@@ -32,8 +34,12 @@
|
||||
},
|
||||
"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:*"
|
||||
}
|
||||
|
||||
@@ -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/**"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# 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)
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
@@ -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,6 +9,7 @@ export default defineWxtModule<SolidModuleOptions>({
|
||||
const { vite } = options ?? {};
|
||||
|
||||
addViteConfig(wxt, () => ({
|
||||
// @ts-expect-error: Vite version mismatch
|
||||
plugins: [solid(vite)],
|
||||
build: {
|
||||
target: 'esnext',
|
||||
@@ -16,6 +17,17 @@ export default defineWxtModule<SolidModuleOptions>({
|
||||
}));
|
||||
|
||||
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}',
|
||||
];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wxt-dev/module-solid",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
@@ -21,8 +21,10 @@
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
"check": "buildc --deps-only -- check",
|
||||
"build": "buildc -- unbuild",
|
||||
"check": "buildc --deps-only -- check"
|
||||
"prepare": "buildc --deps-only -- wxt prepare"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"wxt": ">=0.18.6"
|
||||
@@ -32,8 +34,9 @@
|
||||
},
|
||||
"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:*"
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
},
|
||||
"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:*"
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
},
|
||||
"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:*"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import autoImports from './.wxt/eslintrc-auto-import.js';
|
||||
|
||||
export default [
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...autoImports.globals,
|
||||
},
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "buildc --deps-only -- wxt",
|
||||
"build": "buildc -- wxt build",
|
||||
"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",
|
||||
@@ -21,10 +21,13 @@
|
||||
"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": {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,8 @@ export default defineProject({
|
||||
mockReset: true,
|
||||
restoreMocks: true,
|
||||
},
|
||||
plugins: [WxtVitest()],
|
||||
plugins: [
|
||||
// @ts-expect-error: Vite version mismatch
|
||||
WxtVitest(),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { defineConfig } from 'wxt';
|
||||
|
||||
export default defineConfig({
|
||||
srcDir: 'src',
|
||||
extensionApi: 'chrome',
|
||||
manifest: {
|
||||
permissions: ['storage'],
|
||||
default_locale: 'en',
|
||||
|
||||
@@ -1,5 +1,100 @@
|
||||
# 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)
|
||||
|
||||
@@ -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>
|
||||
•
|
||||
<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>
|
||||
•
|
||||
<a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
|
||||
•
|
||||
<a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
|
||||
•
|
||||
<a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
|
||||
•
|
||||
<a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
|
||||
</p>
|
||||
|
||||

|
||||

|
||||
|
||||
## Demo
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
import '../dist/cli.js';
|
||||
import '../dist/cli/index.mjs';
|
||||
|
||||
@@ -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,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');
|
||||
|
||||
@@ -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']
|
||||
}
|
||||
"
|
||||
`);
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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(),
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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', () => {
|
||||
@@ -192,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);
|
||||
@@ -214,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);
|
||||
@@ -237,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);
|
||||
@@ -255,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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
+58
-63
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.18.10",
|
||||
"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": "buildc -- tsx scripts/build.ts",
|
||||
"build": "buildc -- unbuild",
|
||||
"check": "buildc --deps-only -- run-s -c check:*",
|
||||
"check:default": "check",
|
||||
"check:tsc-virtual": "tsc --noEmit -p src/virtual",
|
||||
"test": "buildc --deps-only -- vitest",
|
||||
"test:e2e": "buildc --deps-only -- vitest -r e2e",
|
||||
"sync-releases": "pnpx changelogen@latest gh release"
|
||||
"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,41 +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",
|
||||
"vite-node": "^1.6.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WxtModule } from '~/types';
|
||||
import { WxtModule } from '../types';
|
||||
import unimport from './unimport';
|
||||
|
||||
export const builtinModules: WxtModule<any>[] = [unimport];
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { addViteConfig, defineWxtModule } from '~/modules';
|
||||
import { addViteConfig, defineWxtModule } from '../modules';
|
||||
import type {
|
||||
EslintGlobalsPropValue,
|
||||
WxtDirFileEntry,
|
||||
WxtModule,
|
||||
WxtResolvedUnimportOptions,
|
||||
} from '~/types';
|
||||
} from '../types';
|
||||
import { type Unimport, createUnimport } from 'unimport';
|
||||
import { Plugin } from 'vite';
|
||||
import { extname } from 'node:path';
|
||||
@@ -30,8 +30,8 @@ export default defineWxtModule({
|
||||
wxt.config.userModules.forEach(addModuleImports);
|
||||
});
|
||||
|
||||
// Create unimport instance after READY so any modifications to the config
|
||||
// inside "ready" are applied.
|
||||
// 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);
|
||||
@@ -45,8 +45,10 @@ export default defineWxtModule({
|
||||
|
||||
entries.push(await getImportsDeclarationEntry(unimport));
|
||||
|
||||
if (!options.eslintrc.enabled) return;
|
||||
entries.push(await getImportsEslintEntry(unimport, options));
|
||||
if (options.eslintrc.enabled === false) return;
|
||||
entries.push(
|
||||
await getEslintConfigEntry(unimport, options.eslintrc.enabled, options),
|
||||
);
|
||||
});
|
||||
|
||||
// Add vite plugin
|
||||
@@ -100,23 +102,49 @@ async function getImportsDeclarationEntry(
|
||||
};
|
||||
}
|
||||
|
||||
async function getImportsEslintEntry(
|
||||
async function getEslintConfigEntry(
|
||||
unimport: Unimport,
|
||||
version: 8 | 9,
|
||||
options: WxtResolvedUnimportOptions,
|
||||
): Promise<WxtDirFileEntry> {
|
||||
const globals: Record<string, EslintGlobalsPropValue> = {};
|
||||
const eslintrc = { globals };
|
||||
|
||||
(await unimport.getImports())
|
||||
const globals = (await unimport.getImports())
|
||||
.map((i) => i.as ?? i.name)
|
||||
.filter(Boolean)
|
||||
.sort()
|
||||
.forEach((name) => {
|
||||
eslintrc.globals[name] = options.eslintrc.globalsPropValue;
|
||||
});
|
||||
.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(eslintrc, null, 2) + '\n',
|
||||
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",
|
||||
},
|
||||
};
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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?:
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
* @module wxt/client
|
||||
*/
|
||||
export * from './content-scripts';
|
||||
export * from './app-config';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BuildOutput, InlineConfig } from '~/types';
|
||||
import { BuildOutput, InlineConfig } from '../types';
|
||||
import { internalBuild } from './utils/building';
|
||||
import { registerWxt } from './wxt';
|
||||
|
||||
|
||||
@@ -7,20 +7,20 @@ import {
|
||||
WxtBuilderServer,
|
||||
WxtDevServer,
|
||||
WxtHooks,
|
||||
} from '~/types';
|
||||
} from '../../../types';
|
||||
import * as wxtPlugins from './plugins';
|
||||
import {
|
||||
getEntrypointBundlePath,
|
||||
isHtmlEntrypoint,
|
||||
} from '~/core/utils/entrypoints';
|
||||
} from '../../utils/entrypoints';
|
||||
import {
|
||||
VirtualEntrypointType,
|
||||
VirtualModuleId,
|
||||
} from '~/core/utils/virtual-modules';
|
||||
} from '../../utils/virtual-modules';
|
||||
import { Hookable } from 'hookable';
|
||||
import { toArray } from '~/core/utils/arrays';
|
||||
import { safeVarName } from '~/core/utils/strings';
|
||||
import { importEntrypointFile } from '~/core/utils/building';
|
||||
import { toArray } from '../../utils/arrays';
|
||||
import { safeVarName } from '../../utils/strings';
|
||||
import { importEntrypointFile } from '../../utils/building';
|
||||
import { ViteNodeServer } from 'vite-node/server';
|
||||
import { ViteNodeRunner } from 'vite-node/client';
|
||||
import { installSourcemapsSupport } from 'vite-node/source-map';
|
||||
@@ -66,9 +66,10 @@ export async function createViteBuilder(
|
||||
wxtPlugins.tsconfigPaths(wxtConfig),
|
||||
wxtPlugins.noopBackground(),
|
||||
wxtPlugins.globals(wxtConfig),
|
||||
wxtPlugins.excludeBrowserPolyfill(wxtConfig),
|
||||
wxtPlugins.resolveExtensionApi(wxtConfig),
|
||||
wxtPlugins.defineImportMeta(),
|
||||
wxtPlugins.wxtPluginLoader(wxtConfig),
|
||||
wxtPlugins.resolveAppConfig(wxtConfig),
|
||||
);
|
||||
if (wxtConfig.analysis.enabled) {
|
||||
config.plugins.push(wxtPlugins.bundleAnalysis(wxtConfig));
|
||||
@@ -217,7 +218,7 @@ export async function createViteBuilder(
|
||||
const baseConfig = await getBaseConfig();
|
||||
const envConfig: vite.InlineConfig = {
|
||||
plugins: [
|
||||
wxtPlugins.webextensionPolyfillMock(wxtConfig),
|
||||
wxtPlugins.extensionApiMock(wxtConfig),
|
||||
wxtPlugins.removeEntrypointMainFunction(wxtConfig, path),
|
||||
],
|
||||
};
|
||||
@@ -237,14 +238,17 @@ export async function createViteBuilder(
|
||||
baseConfig.optimizeDeps.include = [];
|
||||
const envConfig: vite.InlineConfig = {
|
||||
plugins: [
|
||||
wxtPlugins.webextensionPolyfillMock(wxtConfig),
|
||||
wxtPlugins.extensionApiMock(wxtConfig),
|
||||
wxtPlugins.removeEntrypointMainFunction(wxtConfig, path),
|
||||
],
|
||||
};
|
||||
const config = vite.mergeConfig(baseConfig, envConfig);
|
||||
const server = await vite.createServer(config);
|
||||
await server.pluginContainer.buildStart({});
|
||||
const node = new ViteNodeServer(server);
|
||||
const node = new ViteNodeServer(
|
||||
// @ts-ignore: Some weird type error...
|
||||
server,
|
||||
);
|
||||
installSourcemapsSupport({
|
||||
getSourceMap: (source) => node.getSourceMap(source),
|
||||
});
|
||||
|
||||
@@ -4,8 +4,8 @@ import { pointToDevServer } from '../devHtmlPrerender';
|
||||
import {
|
||||
fakeDevServer,
|
||||
fakeResolvedConfig,
|
||||
} from '~/core/utils/testing/fake-objects';
|
||||
import { normalizePath } from '~/core/utils/paths';
|
||||
} from '../../../../utils/testing/fake-objects';
|
||||
import { normalizePath } from '../../../../utils/paths';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
describe('Dev HTML Prerender Plugin', () => {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type * as vite from 'vite';
|
||||
import { visualizer } from '@aklinker1/rollup-plugin-visualizer';
|
||||
import { ResolvedConfig } from '~/types';
|
||||
import { ResolvedConfig } from '../../../../types';
|
||||
import path from 'node:path';
|
||||
|
||||
let increment = 0;
|
||||
|
||||
export function bundleAnalysis(config: ResolvedConfig): vite.Plugin {
|
||||
// @ts-expect-error: Vite version mismatch
|
||||
return visualizer({
|
||||
template: 'raw-data',
|
||||
filename: path.resolve(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type * as vite from 'vite';
|
||||
import { Entrypoint, ResolvedConfig } from '~/types';
|
||||
import { getEntrypointBundlePath } from '~/core/utils/entrypoints';
|
||||
import { Entrypoint, ResolvedConfig } from '../../../../types';
|
||||
import { getEntrypointBundlePath } from '../../../utils/entrypoints';
|
||||
|
||||
/**
|
||||
* Rename CSS entrypoint outputs to ensure a JS file is not generated, and that the CSS file is
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type * as vite from 'vite';
|
||||
import { ResolvedConfig, WxtDevServer } from '~/types';
|
||||
import { getEntrypointName } from '~/core/utils/entrypoints';
|
||||
import { ResolvedConfig, WxtDevServer } from '../../../../types';
|
||||
import { getEntrypointName } from '../../../utils/entrypoints';
|
||||
import { parseHTML } from 'linkedom';
|
||||
import { dirname, relative, resolve } from 'node:path';
|
||||
import { normalizePath } from '~/core/utils/paths';
|
||||
import { normalizePath } from '../../../utils/paths';
|
||||
import { murmurHash } from 'ohash';
|
||||
|
||||
// Stored outside the plugin to effect all instances of the devHtmlPrerender plugin.
|
||||
@@ -19,7 +19,7 @@ export function devHtmlPrerender(
|
||||
const htmlReloadId = '@wxt/reload-html';
|
||||
const resolvedHtmlReloadId = resolve(
|
||||
config.wxtModuleDir,
|
||||
'dist/virtual/reload-html.js',
|
||||
'dist/virtual/reload-html.mjs',
|
||||
);
|
||||
|
||||
const virtualInlineScript = 'virtual:wxt-inline-script';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Plugin } from 'vite';
|
||||
import { ResolvedConfig, WxtDevServer } from '~/types';
|
||||
import { ResolvedConfig, WxtDevServer } from '../../../../types';
|
||||
|
||||
/**
|
||||
* Defines global constants about the dev server. Helps scripts connect to the server's web socket.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Plugin } from 'vite';
|
||||
import { ResolvedConfig } from '~/types';
|
||||
import { fetchCached } from '~/core/utils/network';
|
||||
import { ResolvedConfig } from '../../../../types';
|
||||
import { fetchCached } from '../../../utils/network';
|
||||
|
||||
/**
|
||||
* Downloads any URL imports, like Google Analytics, into virtual modules so they are bundled with
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type * as vite from 'vite';
|
||||
import { EntrypointGroup } from '~/types';
|
||||
import { getEntrypointGlobals } from '~/core/utils/globals';
|
||||
import { EntrypointGroup } from '../../../../types';
|
||||
import { getEntrypointGlobals } from '../../../utils/globals';
|
||||
|
||||
/**
|
||||
* Define a set of global variables specific to an entrypoint.
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { ResolvedConfig } from '~/types';
|
||||
import type * as vite from 'vite';
|
||||
|
||||
/**
|
||||
* Apply the experimental config for disabling the polyfill. It works by aliasing the
|
||||
* `webextension-polyfill` module to a virtual module and exporting the `chrome` global from the
|
||||
* virtual module.
|
||||
*/
|
||||
export function excludeBrowserPolyfill(config: ResolvedConfig): vite.Plugin {
|
||||
const virtualId = 'virtual:wxt-webextension-polyfill-disabled';
|
||||
|
||||
return {
|
||||
name: 'wxt:exclude-browser-polyfill',
|
||||
config() {
|
||||
// Only apply the config if we're disabling the polyfill
|
||||
if (config.experimental.includeBrowserPolyfill) return;
|
||||
|
||||
return {
|
||||
resolve: {
|
||||
alias: {
|
||||
'webextension-polyfill': virtualId,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
load(id) {
|
||||
if (id === virtualId) {
|
||||
// Use chrome instead of the polyfill when disabled.
|
||||
return 'export default chrome';
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user