Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb143d2bbc | |||
| f3e7758521 | |||
| 23ef101954 | |||
| c2f5efbde4 | |||
| 4ce178c592 | |||
| 7aa30afa7f |
@@ -36,7 +36,7 @@ jobs:
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- name: pnpm test:coverage
|
||||
run: pnpm test:coverage -- --reporter=default --reporter=hanging-process
|
||||
- uses: codecov/codecov-action@v4
|
||||
- uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
windows-tests:
|
||||
|
||||
@@ -18,7 +18,7 @@ import { version as storageVersion } from '../../packages/storage/package.json';
|
||||
const title = 'Next-gen Web Extension Framework';
|
||||
const titleSuffix = ' – WXT';
|
||||
const description =
|
||||
"WXT provides the best developer experience, making it quick, easy, and fun to develop chrome extensions for all browsers. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
|
||||
"WXT provides the best developer experience, making it quick, easy, and fun to develop web extensions. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started.";
|
||||
const ogTitle = `${title}${titleSuffix}`;
|
||||
const ogUrl = 'https://wxt.dev';
|
||||
const ogImage = 'https://wxt.dev/social-preview.png';
|
||||
|
||||
@@ -548,16 +548,21 @@ To use `injectScript`, we need two entrypoints, one content script and one unlis
|
||||
```ts
|
||||
// entrypoints/example-main-world.ts
|
||||
export default defineUnlistedScript(() => {
|
||||
console.log('Hello from the main world!');
|
||||
console.log('Hello from the main world');
|
||||
});
|
||||
```
|
||||
|
||||
```ts
|
||||
// entrypoints/example.content.ts
|
||||
export default defineContentScript(async () => {
|
||||
await injectScript('/example-main-world.js', {
|
||||
keepInDom: true,
|
||||
});
|
||||
export default defineContentScript({
|
||||
matches: ['*://*/*'],
|
||||
async main() {
|
||||
console.log('Injecting script...');
|
||||
await injectScript('/example-main-world.js', {
|
||||
keepInDom: true,
|
||||
});
|
||||
console.log('Done!');
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -75,3 +75,5 @@ Here are some examples:
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Alternatively, you can use the [`filterEntrypoints` config](/api/reference/wxt/interfaces/InlineConfig#filterentrypoints) to list all the entrypoints you want to build.
|
||||
|
||||
@@ -9,7 +9,7 @@ Check out the [comparison](/guide/resources/compare) to see how WXT compares to
|
||||
|
||||
## Prerequisites
|
||||
|
||||
These docs assume you have a basic knowledge of how Chrome extensions are structured and how you access the extension APIs.
|
||||
These docs assume you have a basic knowledge of how web extensions are structured and how you access the extension APIs.
|
||||
|
||||
:::warning New to extension development?
|
||||
If you have never written an extension before, follow Chrome's [Hello World tutorial](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world) to first **_create an extension without WXT_**, then come back here.
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ title: Next-gen Web Extension Framework
|
||||
hero:
|
||||
name: WXT
|
||||
text: Next-gen Web Extension Framework
|
||||
tagline: An open source tool that makes Chrome Extension development faster than ever before.
|
||||
tagline: An open source tool that makes web extension development faster than ever before.
|
||||
image:
|
||||
src: /hero-logo.svg
|
||||
alt: WXT
|
||||
@@ -75,7 +75,7 @@ features:
|
||||
|
||||
## Put <span style="color: var(--vp-c-brand-1)">Developer Experience</span> First
|
||||
|
||||
WXT simplifies the chrome extension development process by providing tools for zipping and publishing, the best-in-class dev mode, an opinionated project structure, and more. Iterate faster, develop features not build scripts, and use everything the JS ecosystem has to offer.
|
||||
WXT simplifies the web extension development process by providing tools for zipping and publishing, the best-in-class dev mode, an opinionated project structure, and more. Iterate faster, develop features not build scripts, and use everything the JS ecosystem has to offer.
|
||||
|
||||
<div style="margin: auto; width: 100%; max-width: 900px; text-align: center">
|
||||
<video src="https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94" controls></video>
|
||||
@@ -87,7 +87,7 @@ WXT simplifies the chrome extension development process by providing tools for z
|
||||
|
||||
## Who's Using WXT?
|
||||
|
||||
Battle tested and ready for production. Explore chrome extensions made with WXT.
|
||||
Battle tested and ready for production. Explore web extensions made with WXT.
|
||||
|
||||
<ClientOnly>
|
||||
<UsingWxtSection />
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## v0.19.18
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.17...wxt-v0.19.18)
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Correct `dev.reloadCommand` restriction to consider only commands with suggested keys ([#1226](https://github.com/wxt-dev/wxt/pull/1226))
|
||||
|
||||
### 🌊 Types
|
||||
|
||||
- Add overloads to `ContentScriptContext#addEventListener` for different targets ([#1245](https://github.com/wxt-dev/wxt/pull/1245))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Refactor `findEntrypoints` to return all entrypoints with `skipped` set properly ([#1244](https://github.com/wxt-dev/wxt/pull/1244))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron ([@aklinker1](http://github.com/aklinker1))
|
||||
- Jaguar Zhou ([@aiktb](http://github.com/aiktb))
|
||||
|
||||
## v0.19.17
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.16...wxt-v0.19.17)
|
||||
@@ -2667,4 +2688,4 @@ Initial release of WXT. Full support for production builds and initial toolkit f
|
||||
### 🤖 CI
|
||||
|
||||
- Create validation workflow ([#12](https://github.com/wxt-dev/wxt/pull/12))
|
||||
- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13))
|
||||
- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13))
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.19.17",
|
||||
"version": "0.19.18",
|
||||
"description": "Next gen framework for developing web extensions",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ContentScriptContext } from '..';
|
||||
import { fakeBrowser } from '@webext-core/fake-browser';
|
||||
import { sleep } from '../../../core/utils/time';
|
||||
|
||||
/**
|
||||
* When dispatching events on document/window/etc, they are fired on the next
|
||||
* tick of the event loop. So waiting a timeout of 0 will ensure they've been
|
||||
* fired.
|
||||
*/
|
||||
function waitForEventsToFire() {
|
||||
return new Promise((res) => setTimeout(res));
|
||||
}
|
||||
|
||||
describe('Content Script Context', () => {
|
||||
beforeEach(() => {
|
||||
@@ -31,11 +39,11 @@ describe('Content Script Context', () => {
|
||||
ctx.onInvalidated(onInvalidated);
|
||||
|
||||
// Wait for events to run before next tick next tick
|
||||
await sleep(0);
|
||||
await waitForEventsToFire();
|
||||
|
||||
// Create a new context after first is initialized, and wait for it to initialize
|
||||
new ContentScriptContext(name);
|
||||
await sleep(0);
|
||||
await waitForEventsToFire();
|
||||
|
||||
expect(onInvalidated).toBeCalled();
|
||||
expect(ctx.isValid).toBe(false);
|
||||
@@ -47,13 +55,35 @@ describe('Content Script Context', () => {
|
||||
ctx.onInvalidated(onInvalidated);
|
||||
|
||||
// Wait for events to run before next tick next tick
|
||||
await sleep(0);
|
||||
await waitForEventsToFire();
|
||||
|
||||
// Create a new context after first is initialized, and wait for it to initialize
|
||||
new ContentScriptContext('test2');
|
||||
await sleep(0);
|
||||
await waitForEventsToFire();
|
||||
|
||||
expect(onInvalidated).not.toBeCalled();
|
||||
expect(ctx.isValid).toBe(true);
|
||||
});
|
||||
|
||||
describe('addEventListener', () => {
|
||||
const context = new ContentScriptContext('test');
|
||||
it('should infer types correctly for the window target', () => {
|
||||
context.addEventListener(window, 'DOMContentLoaded', (_) => {});
|
||||
context.addEventListener(window, 'orientationchange', (_) => {});
|
||||
context.addEventListener(window, 'wxt:locationchange', (_) => {});
|
||||
// @ts-expect-error
|
||||
context.addEventListener(window, 'visibilitychange', (_) => {});
|
||||
});
|
||||
|
||||
it('should infer types correctly for the document target', () => {
|
||||
context.addEventListener(document, 'visibilitychange', (_) => {});
|
||||
context.addEventListener(document, 'readystatechange', (_) => {});
|
||||
});
|
||||
|
||||
it('should infer types correctly for HTML element targets', () => {
|
||||
const button = document.createElement('button');
|
||||
context.addEventListener(button, 'click', (_) => {});
|
||||
context.addEventListener(button, 'mouseover', (_) => {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -175,19 +175,32 @@ export class ContentScriptContext implements AbortController {
|
||||
* ctx.addEventListener(document, "visibilitychange", () => {
|
||||
* // ...
|
||||
* });
|
||||
* ctx.addEventListener(document, "wxt:locationchange", () => {
|
||||
* ctx.addEventListener(window, "wxt:locationchange", () => {
|
||||
* // ...
|
||||
* });
|
||||
*/
|
||||
addEventListener<
|
||||
TTarget extends EventTarget,
|
||||
TType extends keyof WxtContentScriptEventMap,
|
||||
>(
|
||||
target: TTarget,
|
||||
addEventListener<TType extends keyof WxtWindowEventMap>(
|
||||
target: Window,
|
||||
type: TType,
|
||||
handler: (event: WxtContentScriptEventMap[TType]) => void,
|
||||
handler: (event: WxtWindowEventMap[TType]) => void,
|
||||
options?: AddEventListenerOptions,
|
||||
) {
|
||||
): void;
|
||||
addEventListener<TType extends keyof DocumentEventMap>(
|
||||
target: Document,
|
||||
type: keyof DocumentEventMap,
|
||||
handler: (event: DocumentEventMap[TType]) => void,
|
||||
options?: AddEventListenerOptions,
|
||||
): void;
|
||||
addEventListener<TTarget extends EventTarget>(
|
||||
target: TTarget,
|
||||
...params: Parameters<TTarget['addEventListener']>
|
||||
): void;
|
||||
addEventListener(
|
||||
target: EventTarget,
|
||||
type: string,
|
||||
handler: (event: Event) => void,
|
||||
options?: AddEventListenerOptions,
|
||||
): void {
|
||||
if (type === 'wxt:locationchange') {
|
||||
// Start the location watcher when adding the event for the first time
|
||||
if (this.isValid) this.locationWatcher.run();
|
||||
@@ -195,7 +208,6 @@ export class ContentScriptContext implements AbortController {
|
||||
|
||||
target.addEventListener?.(
|
||||
type.startsWith('wxt:') ? getUniqueEventName(type) : type,
|
||||
// @ts-expect-error: Event don't match, but that's OK, EventTarget doesn't allow custom types in the callback
|
||||
handler,
|
||||
{
|
||||
...options,
|
||||
@@ -247,6 +259,6 @@ export class ContentScriptContext implements AbortController {
|
||||
}
|
||||
}
|
||||
|
||||
interface WxtContentScriptEventMap extends WindowEventMap {
|
||||
export interface WxtWindowEventMap extends WindowEventMap {
|
||||
'wxt:locationchange': WxtLocationChangeEvent;
|
||||
}
|
||||
|
||||
@@ -701,7 +701,7 @@ describe('findEntrypoints', () => {
|
||||
});
|
||||
|
||||
describe('include option', () => {
|
||||
it("should filter out the background when include doesn't contain the target browser", async () => {
|
||||
it("should mark the background as skipped when include doesn't contain the target browser", async () => {
|
||||
globMock.mockResolvedValueOnce(['background.ts']);
|
||||
importEntrypointMock.mockResolvedValue({
|
||||
include: ['not' + config.browser],
|
||||
@@ -709,10 +709,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'background',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("should filter out content scripts when include doesn't contain the target browser", async () => {
|
||||
it("should mark content scripts as skipped when include doesn't contain the target browser", async () => {
|
||||
globMock.mockResolvedValueOnce(['example.content.ts']);
|
||||
importEntrypointMock.mockResolvedValue({
|
||||
include: ['not' + config.browser],
|
||||
@@ -720,10 +725,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'example',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("should filter out the popup when include doesn't contain the target browser", async () => {
|
||||
it("should mark the popup as skipped when include doesn't contain the target browser", async () => {
|
||||
globMock.mockResolvedValueOnce(['popup.html']);
|
||||
readFileMock.mockResolvedValueOnce(
|
||||
`<html>
|
||||
@@ -737,10 +747,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'popup',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("should filter out the options page when include doesn't contain the target browser", async () => {
|
||||
it("should mark the options page as skipped when include doesn't contain the target browser", async () => {
|
||||
globMock.mockResolvedValueOnce(['options.html']);
|
||||
readFileMock.mockResolvedValueOnce(
|
||||
`<html>
|
||||
@@ -754,10 +769,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'options',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("should filter out an unlisted page when include doesn't contain the target browser", async () => {
|
||||
it("should mark unlisted pages as skipped when include doesn't contain the target browser", async () => {
|
||||
globMock.mockResolvedValueOnce(['unlisted.html']);
|
||||
readFileMock.mockResolvedValueOnce(
|
||||
`<html>
|
||||
@@ -771,12 +791,17 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'unlisted',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('exclude option', () => {
|
||||
it('should filter out the background when exclude contains the target browser', async () => {
|
||||
it('should mark the background as skipped when exclude contains the target browser', async () => {
|
||||
globMock.mockResolvedValueOnce(['background.ts']);
|
||||
importEntrypointMock.mockResolvedValue({
|
||||
exclude: [config.browser],
|
||||
@@ -784,10 +809,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'background',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter out content scripts when exclude contains the target browser', async () => {
|
||||
it('should mark content scripts as skipped when exclude contains the target browser', async () => {
|
||||
globMock.mockResolvedValueOnce(['example.content.ts']);
|
||||
importEntrypointMock.mockResolvedValue({
|
||||
exclude: [config.browser],
|
||||
@@ -795,10 +825,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'example',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter out the popup when exclude contains the target browser', async () => {
|
||||
it('should mark the popup as skipped when exclude contains the target browser', async () => {
|
||||
globMock.mockResolvedValueOnce(['popup.html']);
|
||||
readFileMock.mockResolvedValueOnce(
|
||||
`<html>
|
||||
@@ -810,10 +845,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'popup',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter out the options page when exclude contains the target browser', async () => {
|
||||
it('should mark the options page as skipped when exclude contains the target browser', async () => {
|
||||
globMock.mockResolvedValueOnce(['options.html']);
|
||||
readFileMock.mockResolvedValueOnce(
|
||||
`<html>
|
||||
@@ -825,10 +865,15 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'options',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter out an unlisted page when exclude contains the target browser', async () => {
|
||||
it('should mark unlisted pages as skipped when exclude contains the target browser', async () => {
|
||||
globMock.mockResolvedValueOnce(['unlisted.html']);
|
||||
readFileMock.mockResolvedValueOnce(
|
||||
`<html>
|
||||
@@ -840,12 +885,17 @@ describe('findEntrypoints', () => {
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toEqual([]);
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'unlisted',
|
||||
skipped: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterEntrypoints option', () => {
|
||||
it('should control entrypoints accessible', async () => {
|
||||
it('should override include/exclude of individual entrypoint options', async () => {
|
||||
globMock.mockResolvedValue([
|
||||
'options/index.html',
|
||||
'popup/index.html',
|
||||
@@ -867,9 +917,25 @@ describe('findEntrypoints', () => {
|
||||
importEntrypointMock.mockResolvedValue({});
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
const names = entrypoints.map((item) => item.name);
|
||||
expect(names).toHaveLength(2);
|
||||
expect(names).toEqual(filterEntrypoints);
|
||||
|
||||
expect(entrypoints).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'injected',
|
||||
skipped: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'options',
|
||||
skipped: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'popup',
|
||||
skipped: false,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'ui',
|
||||
skipped: false,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -152,12 +152,16 @@ describe('groupEntrypoints', () => {
|
||||
options: {
|
||||
type: 'module',
|
||||
},
|
||||
skipped: false,
|
||||
});
|
||||
const popup = fakePopupEntrypoint({
|
||||
skipped: false,
|
||||
});
|
||||
const popup = fakePopupEntrypoint();
|
||||
const sandbox = fakeGenericEntrypoint({
|
||||
inputPath: '/entrypoints/sandbox.html',
|
||||
name: 'sandbox',
|
||||
type: 'sandbox',
|
||||
skipped: false,
|
||||
});
|
||||
|
||||
const actual = groupEntrypoints([background, popup, sandbox]);
|
||||
@@ -165,6 +169,22 @@ describe('groupEntrypoints', () => {
|
||||
expect(actual).toEqual([[background, popup], [sandbox]]);
|
||||
});
|
||||
|
||||
it('should exclude skipped entrypoints from the groups to build', () => {
|
||||
const background = fakeBackgroundEntrypoint({
|
||||
options: {
|
||||
type: 'module',
|
||||
},
|
||||
skipped: false,
|
||||
});
|
||||
const popup = fakePopupEntrypoint({
|
||||
skipped: true,
|
||||
});
|
||||
|
||||
const actual = groupEntrypoints([background, popup]);
|
||||
|
||||
expect(actual).toEqual([[background]]);
|
||||
});
|
||||
|
||||
it.todo(
|
||||
'should group ESM compatible sandbox scripts with sandbox pages',
|
||||
() => {
|
||||
|
||||
@@ -65,14 +65,7 @@ export async function findEntrypoints(): Promise<Entrypoint[]> {
|
||||
);
|
||||
if (matchingGlob) {
|
||||
const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
|
||||
results.push({
|
||||
name,
|
||||
inputPath,
|
||||
type,
|
||||
skipped:
|
||||
wxt.config.filterEntrypoints != null &&
|
||||
!wxt.config.filterEntrypoints.has(name),
|
||||
});
|
||||
results.push({ name, inputPath, type });
|
||||
}
|
||||
return results;
|
||||
}, []);
|
||||
@@ -84,7 +77,7 @@ export async function findEntrypoints(): Promise<Entrypoint[]> {
|
||||
// Import entrypoints to get their config
|
||||
let hasBackground = false;
|
||||
const env = createExtensionEnvironment();
|
||||
const entrypoints: Entrypoint[] = await env.run(() =>
|
||||
const entrypointsWithoutSkipped: Entrypoint[] = await env.run(() =>
|
||||
Promise.all(
|
||||
entrypointInfos.map(async (info): Promise<Entrypoint> => {
|
||||
const { type } = info;
|
||||
@@ -130,61 +123,44 @@ export async function findEntrypoints(): Promise<Entrypoint[]> {
|
||||
);
|
||||
|
||||
if (wxt.config.command === 'serve' && !hasBackground) {
|
||||
entrypoints.push(
|
||||
entrypointsWithoutSkipped.push(
|
||||
await getBackgroundEntrypoint({
|
||||
inputPath: VIRTUAL_NOOP_BACKGROUND_MODULE_ID,
|
||||
name: 'background',
|
||||
type: 'background',
|
||||
skipped: false,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Mark entrypoints as skipped or not
|
||||
const entrypoints = entrypointsWithoutSkipped.map((entry) => ({
|
||||
...entry,
|
||||
skipped: isEntrypointSkipped(entry),
|
||||
}));
|
||||
|
||||
wxt.logger.debug('All entrypoints:', entrypoints);
|
||||
const skippedEntrypointNames = entrypointInfos
|
||||
const skippedEntrypointNames = entrypoints
|
||||
.filter((item) => item.skipped)
|
||||
.map((item) => item.name);
|
||||
if (skippedEntrypointNames.length) {
|
||||
wxt.logger.warn(
|
||||
`Filter excluded the following entrypoints:\n${skippedEntrypointNames
|
||||
.map((item) => `${pc.dim('-')} ${pc.cyan(item)}`)
|
||||
.join('\n')}`,
|
||||
[
|
||||
'The following entrypoints have been skipped:',
|
||||
...skippedEntrypointNames.map(
|
||||
(item) => `${pc.dim('-')} ${pc.cyan(item)}`,
|
||||
),
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
const targetEntrypoints = entrypoints.filter((entry) => {
|
||||
const { include, exclude } = entry.options;
|
||||
if (include?.length && exclude?.length) {
|
||||
wxt.logger.warn(
|
||||
`The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (exclude?.length && !include?.length) {
|
||||
return !exclude.includes(wxt.config.browser);
|
||||
}
|
||||
if (include?.length && !exclude?.length) {
|
||||
return include.includes(wxt.config.browser);
|
||||
}
|
||||
if (skippedEntrypointNames.includes(entry.name)) {
|
||||
return false;
|
||||
}
|
||||
await wxt.hooks.callHook('entrypoints:resolved', wxt, entrypoints);
|
||||
|
||||
return true;
|
||||
});
|
||||
wxt.logger.debug(`${wxt.config.browser} entrypoints:`, targetEntrypoints);
|
||||
await wxt.hooks.callHook('entrypoints:resolved', wxt, targetEntrypoints);
|
||||
|
||||
return targetEntrypoints;
|
||||
return entrypoints;
|
||||
}
|
||||
|
||||
interface EntrypointInfo {
|
||||
name: string;
|
||||
inputPath: string;
|
||||
type: Entrypoint['type'];
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
skipped: boolean;
|
||||
}
|
||||
|
||||
function preventDuplicateEntrypointNames(files: EntrypointInfo[]) {
|
||||
@@ -252,7 +228,6 @@ async function getPopupEntrypoint(
|
||||
options: resolvePerBrowserOptions(options, wxt.config.browser),
|
||||
inputPath: info.inputPath,
|
||||
outputDir: wxt.config.outDir,
|
||||
skipped: info.skipped,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -275,7 +250,6 @@ async function getOptionsEntrypoint(
|
||||
options: resolvePerBrowserOptions(options, wxt.config.browser),
|
||||
inputPath: info.inputPath,
|
||||
outputDir: wxt.config.outDir,
|
||||
skipped: info.skipped,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -293,14 +267,12 @@ async function getUnlistedPageEntrypoint(
|
||||
inputPath: info.inputPath,
|
||||
outputDir: wxt.config.outDir,
|
||||
options,
|
||||
skipped: info.skipped,
|
||||
};
|
||||
}
|
||||
|
||||
async function getUnlistedScriptEntrypoint({
|
||||
inputPath,
|
||||
name,
|
||||
skipped,
|
||||
}: EntrypointInfo): Promise<GenericEntrypoint> {
|
||||
const defaultExport =
|
||||
await wxt.builder.importEntrypoint<UnlistedScriptDefinition>(inputPath);
|
||||
@@ -316,14 +288,12 @@ async function getUnlistedScriptEntrypoint({
|
||||
inputPath,
|
||||
outputDir: wxt.config.outDir,
|
||||
options: resolvePerBrowserOptions(options, wxt.config.browser),
|
||||
skipped,
|
||||
};
|
||||
}
|
||||
|
||||
async function getBackgroundEntrypoint({
|
||||
inputPath,
|
||||
name,
|
||||
skipped,
|
||||
}: EntrypointInfo): Promise<BackgroundEntrypoint> {
|
||||
let options: Omit<BackgroundDefinition, 'main'> = {};
|
||||
if (inputPath !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
|
||||
@@ -348,14 +318,12 @@ async function getBackgroundEntrypoint({
|
||||
inputPath,
|
||||
outputDir: wxt.config.outDir,
|
||||
options: resolvePerBrowserOptions(options, wxt.config.browser),
|
||||
skipped,
|
||||
};
|
||||
}
|
||||
|
||||
async function getContentScriptEntrypoint({
|
||||
inputPath,
|
||||
name,
|
||||
skipped,
|
||||
}: EntrypointInfo): Promise<ContentScriptEntrypoint> {
|
||||
const defaultExport =
|
||||
await wxt.builder.importEntrypoint<ContentScriptDefinition>(inputPath);
|
||||
@@ -377,7 +345,6 @@ async function getContentScriptEntrypoint({
|
||||
inputPath,
|
||||
outputDir: resolve(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
|
||||
options: resolvePerBrowserOptions(options, wxt.config.browser),
|
||||
skipped,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -409,7 +376,6 @@ async function getSidepanelEntrypoint(
|
||||
options: resolvePerBrowserOptions(options, wxt.config.browser),
|
||||
inputPath: info.inputPath,
|
||||
outputDir: wxt.config.outDir,
|
||||
skipped: info.skipped,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -457,6 +423,29 @@ async function getHtmlEntrypointOptions<T extends BaseEntrypointOptions>(
|
||||
return options;
|
||||
}
|
||||
|
||||
function isEntrypointSkipped(entry: Omit<Entrypoint, 'skipped'>): boolean {
|
||||
if (wxt.config.filterEntrypoints != null) {
|
||||
return !wxt.config.filterEntrypoints.has(entry.name);
|
||||
}
|
||||
|
||||
const { include, exclude } = entry.options;
|
||||
if (include?.length && exclude?.length) {
|
||||
wxt.logger.warn(
|
||||
`The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint skipped.`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (exclude?.length && !include?.length) {
|
||||
return exclude.includes(wxt.config.browser);
|
||||
}
|
||||
if (include?.length && !exclude?.length) {
|
||||
return !include.includes(wxt.config.browser);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const PATH_GLOB_TO_TYPE_MAP: Record<string, Entrypoint['type']> = {
|
||||
'sandbox.html': 'sandbox',
|
||||
'sandbox/index.html': 'sandbox',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Entrypoint, EntrypointGroup } from '../../../types';
|
||||
|
||||
/**
|
||||
* Entrypoints can be build in groups. HTML pages can all be built together in a single step, while
|
||||
* content scripts must be build individually.
|
||||
* Entrypoints are built in groups. HTML pages can all be built together in a single step,
|
||||
* content scripts must be build individually, etc.
|
||||
*
|
||||
* This function returns the entrypoints put into these types of groups.
|
||||
*/
|
||||
@@ -11,6 +11,8 @@ export function groupEntrypoints(entrypoints: Entrypoint[]): EntrypointGroup[] {
|
||||
const groups: EntrypointGroup[] = [];
|
||||
|
||||
for (const entry of entrypoints) {
|
||||
if (entry.skipped) continue;
|
||||
|
||||
let group = ENTRY_TYPE_TO_GROUP_MAP[entry.type];
|
||||
if (entry.type === 'background' && entry.options.type === 'module') {
|
||||
group = 'esm';
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface InlineConfig {
|
||||
* A list of entrypoint names (`"popup"`, `"options"`, etc.) to build. Will speed up the build if
|
||||
* your extension has lots of entrypoints, and you don't need to build all of them to develop a
|
||||
* feature.
|
||||
* If specified, this completely overrides the `include`/`exclude` option provided per-entrypoint.
|
||||
*/
|
||||
filterEntrypoints?: string[];
|
||||
/**
|
||||
@@ -728,7 +729,14 @@ export interface BaseEntrypoint {
|
||||
* subdirectory of it.
|
||||
*/
|
||||
outputDir: string;
|
||||
skipped: boolean;
|
||||
/**
|
||||
* When true, the entrypoint will not be built by WXT. Normally this is set
|
||||
* based on the `filterEntrypoints` config or the entrypoint's
|
||||
* `include`/`exclude` options defined inside the file.
|
||||
*
|
||||
* See https://wxt.dev/guide/essentials/target-different-browsers.html#filtering-entrypoints
|
||||
*/
|
||||
skipped?: boolean;
|
||||
}
|
||||
|
||||
export interface GenericEntrypoint extends BaseEntrypoint {
|
||||
|
||||
Reference in New Issue
Block a user