chore: Add prettier-plugin-jsdoc to project (#2171)

This commit is contained in:
Aaron
2026-03-03 08:43:04 -06:00
committed by GitHub
parent 19cebbf673
commit 301e4a17da
95 changed files with 1170 additions and 1084 deletions
+2
View File
@@ -1,2 +1,4 @@
singleQuote: true
endOfLine: lf
plugins:
- prettier-plugin-jsdoc
+1 -3
View File
@@ -77,9 +77,7 @@ export function menuItem(
return { text, link };
}
/**
* Clean up and add badges to typedoc leaf sections
*/
/** Clean up and add badges to typedoc leaf sections */
export function prepareTypedocSidebar(items: SidebarItem[]) {
// skip contents file
const filtered = items.slice(1);
+1
View File
@@ -34,6 +34,7 @@
"nano-spawn": "^2.0.0",
"p-map": "^7.0.4",
"prettier": "^3.8.1",
"prettier-plugin-jsdoc": "^1.8.0",
"semver": "^7.7.4",
"simple-git-hooks": "^2.13.1",
"tsdown": "^0.20.3",
@@ -62,7 +62,8 @@ export function createAnalytics(config?: AnalyticsConfig): Analytics {
}
/**
* Creates an analytics client in the background responsible for uploading events to the server to avoid CORS errors.
* Creates an analytics client in the background responsible for uploading
* events to the server to avoid CORS errors.
*/
function createBackgroundAnalytics(
config: AnalyticsConfig | undefined,
@@ -219,9 +220,7 @@ function createBackgroundAnalytics(
return analytics;
}
/**
* Creates an analytics client for non-background contexts.
*/
/** Creates an analytics client for non-background contexts. */
function createFrontendAnalytics(): Analytics {
const port = browser.runtime.connect({ name: ANALYTICS_PORT });
const sessionId = Date.now();
+13 -10
View File
@@ -8,36 +8,39 @@ export interface Analytics {
) => void;
/** Save information about the user. */
identify: (userId: string, userProperties?: Record<string, string>) => void;
/** Automatically setup and track user interactions, returning a function to remove any listeners that were setup. */
/**
* Automatically setup and track user interactions, returning a function to
* remove any listeners that were setup.
*/
autoTrack: (root: Document | ShadowRoot | Element) => () => void;
/** Calls `config.enabled.setValue`. */
setEnabled: (enabled: boolean) => void;
}
export interface AnalyticsConfig {
/**
* Array of providers to send analytics to.
*/
/** Array of providers to send analytics to. */
providers: AnalyticsProvider[];
/**
* Enable debug logs and other provider-specific debugging features.
*/
/** Enable debug logs and other provider-specific debugging features. */
debug?: boolean;
/**
* Your extension's version, reported alongside events.
*
* @default browser.runtime.getManifest().version`.
*/
version?: string;
/**
* Configure how the enabled flag is persisted. Defaults to using `browser.storage.local`.
* Configure how the enabled flag is persisted. Defaults to using
* `browser.storage.local`.
*/
enabled?: AnalyticsStorageItem<boolean>;
/**
* Configure how the user Id is persisted. Defaults to using `browser.storage.local`.
* Configure how the user Id is persisted. Defaults to using
* `browser.storage.local`.
*/
userId?: AnalyticsStorageItem<string | undefined>;
/**
* Configure how user properties are persisted. Defaults to using `browser.storage.local`.
* Configure how user properties are persisted. Defaults to using
* `browser.storage.local`.
*/
userProperties?: AnalyticsStorageItem<Record<string, string>>;
}
+11 -9
View File
@@ -109,12 +109,11 @@ export default defineWxtModule<AutoIconsOptions>({
},
});
/**
* Options for the auto-icons module
*/
/** Options for the auto-icons module */
export interface AutoIconsOptions {
/**
* Enable auto-icons generation
*
* @default true
*/
enabled?: boolean;
@@ -122,27 +121,30 @@ export interface AutoIconsOptions {
* Path to the image to use.
*
* Path is relative to the project's src directory.
* @default "<srcDir>/assets/icon.png"
*
* @default '<srcDir>/assets/icon.png'
*/
baseIconPath?: string;
/**
* Apply a visual indicator to the icon when running in development mode.
*
* "grayscale" converts the icon to grayscale.
* "overlay" covers the bottom half with a yellow rectangle and writes "DEV" in black text.
* Set to `false` to disable any indicator.
* "grayscale" converts the icon to grayscale. "overlay" covers the bottom
* half with a yellow rectangle and writes "DEV" in black text. Set to `false`
* to disable any indicator.
*
* @default "grayscale"
* @default 'grayscale'
*/
developmentIndicator?: 'grayscale' | 'overlay' | false;
/**
* Grayscale the image when in development mode to indicate development
* @default true
*
* @deprecated Use `developmentIndicator` instead
* @default true
*/
grayscaleOnDevelopment?: boolean;
/**
* Sizes to generate icons for
*
* @default [128, 48, 32, 16]
*/
sizes?: number[];
+5 -10
View File
@@ -51,7 +51,8 @@ export type MessageFormat = 'JSON5' | 'YAML' | 'TOML';
//
/**
* See https://developer.chrome.com/docs/extensions/reference/api/i18n#overview-predefined
* See
* https://developer.chrome.com/docs/extensions/reference/api/i18n#overview-predefined
*/
const PREDEFINED_MESSAGES: Record<string, ChromeMessage> = {
'@@extension_id': {
@@ -110,9 +111,7 @@ const ALLOWED_CHROME_MESSAGE_KEYS: Set<string> = new Set<keyof ChromeMessage>([
// PARSING
//
/**
* Parse a messages file, extract the messages. Supports JSON, JSON5, and YAML.
*/
/** Parse a messages file, extract the messages. Supports JSON, JSON5, and YAML. */
export async function parseMessagesFile(
file: string,
): Promise<ParsedMessage[]> {
@@ -121,9 +120,7 @@ export async function parseMessagesFile(
return parseMessagesText(text, EXT_FORMATS_MAP[ext] ?? 'JSON5');
}
/**
* Parse a string, extracting the messages. Supports JSON, JSON5, and YAML.
*/
/** Parse a string, extracting the messages. Supports JSON, JSON5, and YAML. */
export function parseMessagesText(
text: string,
format: 'JSON5' | 'YAML' | 'TOML',
@@ -131,9 +128,7 @@ export function parseMessagesText(
return parseMessagesObject(PARSERS[format](text));
}
/**
* Given the JS object form of a raw messages file, extract the messages.
*/
/** Given the JS object form of a raw messages file, extract the messages. */
export function parseMessagesObject(object: any): ParsedMessage[] {
return _parseMessagesObject(
[],
+1 -3
View File
@@ -1,6 +1,4 @@
/**
* @module @wxt-dev/i18n
*/
/** @module @wxt-dev/i18n */
import {
I18nStructure,
DefaultI18nStructure,
+4 -5
View File
@@ -3,7 +3,7 @@
*
* ```ts
* export default defineConfig({
* modules: ["@wxt-dev/i18n/module"],
* modules: ['@wxt-dev/i18n/module'],
* });
* ```
*
@@ -180,13 +180,12 @@ export { type GeneratedI18nStructure }
},
});
/**
* Options for the i18n module
*/
/** Options for the i18n module */
export interface I18nOptions {
/**
* Directory containing files that define the translations.
* @default "${config.srcDir}/locales"
*
* @default '${config.srcDir}/locales'
*/
localesDir?: string;
}
+10 -6
View File
@@ -1,10 +1,14 @@
/**
* This module uses a lazy getter function so the logic isn't ran until it's needed.
* This module uses a lazy getter function so the logic isn't ran until it's
* needed.
*
* This has a few benefits:
*
* 1. Easier to mock in tests
* 2. Safe to import in NodeJS environments (but it should be safe to run just in-case)
* 3. Keeps startup fast by waiting to run the slow functions (`instanceof` or `browser.extension.getBackgroundPage`) until needed
* 2. Safe to import in NodeJS environments (but it should be safe to run just
* in-case)
* 3. Keeps startup fast by waiting to run the slow functions (`instanceof` or
* `browser.extension.getBackgroundPage`) until needed
*
* @module @wxt-dev/is-background
*/
@@ -16,10 +20,10 @@ let cached: boolean | undefined;
* Getter that returns if the current context is apart of an extension's
* background or not.
*
* > This function caches the result when called for the first time so it
* > doesn't have to recalculate.
* > This function caches the result when called for the first time so it doesn't
* > have to recalculate.
*
* @returns true when in a background page or service worker.
* @returns True when in a background page or service worker.
*/
export function isBackground(): boolean {
if (cached == null) cached = getIsBackground();
+2 -1
View File
@@ -124,7 +124,8 @@ export const KNOWN_BROWSER_PATHS: Record<
};
/**
* When targeting a browser, this map contains the other targets to fall back on when a binary could not be found for the primary target.
* When targeting a browser, this map contains the other targets to fall back on
* when a binary could not be found for the primary target.
*/
export const FALLBACK_TARGETS: Partial<Record<KnownTarget, KnownTarget[]>> = {
chrome: [
+7 -3
View File
@@ -4,8 +4,11 @@ import { createCdpConnection } from './cdp';
/**
* Install an extension to an already running instance of Firefox.
* @param debuggerUrl The URL of the Firefox BiDi server (ex: `ws://127.0.0.1:45912`).
* @param extensionDir Absolute path to the directory containing the extension to be installed.
*
* @param debuggerUrl The URL of the Firefox BiDi server (ex:
* `ws://127.0.0.1:45912`).
* @param extensionDir Absolute path to the directory containing the extension
* to be installed.
*/
export async function installFirefox(
debuggerUrl: string,
@@ -33,7 +36,8 @@ export type BidiWebExtensionInstallResponse = {
};
/**
* Given a child process of Chrome, install an extension. The process must be started with the following flags:
* Given a child process of Chrome, install an extension. The process must be
* started with the following flags:
*
* - `--remote-debugging-pipe`
* - `--user-data-dir=...`
+36 -10
View File
@@ -17,21 +17,46 @@ export type Target = KnownTarget | UnknownTarget;
export type RunOptions = {
/** Paths to binaries to use for each target. */
browserBinaries?: Record<string, string>;
/** Customize the arguments passed to the chromium binary. Conflicting arguments with required ones to install extensions are ignored. */
/**
* Customize the arguments passed to the chromium binary. Conflicting
* arguments with required ones to install extensions are ignored.
*/
chromiumArgs?: string[];
/** Control how data is persisted between launches. Either save data at a user level, project level, or don't persist data at all. Defaults to `project`. */
/**
* Control how data is persisted between launches. Either save data at a user
* level, project level, or don't persist data at all. Defaults to `project`.
*/
dataPersistence?: 'user' | 'project' | 'none';
/** Customize where your profile's data is stored when using `dataPersistence: 'project'`. Can be absolute or relative to the current working directory. */
/**
* Customize where your profile's data is stored when using `dataPersistence:
* 'project'`. Can be absolute or relative to the current working directory.
*/
projectDataDir?: string;
/** Customize the port Chrome's debugger is listening on. Defaults to a random open port. */
/**
* Customize the port Chrome's debugger is listening on. Defaults to a random
* open port.
*/
chromiumRemoteDebuggingPort?: number;
/** Directory where the extension will be installed from. Should contain a `manifest.json` file. Can be relative to the current working directory. Defaults to the current working directory. */
/**
* Directory where the extension will be installed from. Should contain a
* `manifest.json` file. Can be relative to the current working directory.
* Defaults to the current working directory.
*/
extensionDir?: string;
/** Customize the arguments passed to the firefox binary. Conflicting arguments with required ones to install extensions are ignored. */
/**
* Customize the arguments passed to the firefox binary. Conflicting arguments
* with required ones to install extensions are ignored.
*/
firefoxArgs?: string[];
/** Customize the port Firefox's debugger is listening on. Defaults to a random open port. */
/**
* Customize the port Firefox's debugger is listening on. Defaults to a random
* open port.
*/
firefoxRemoteDebuggingPort?: number;
/** Specify the browser to open. Defaults to `"chrome"`, but you can pass any string. */
/**
* Specify the browser to open. Defaults to `"chrome"`, but you can pass any
* string.
*/
target?: Target;
};
@@ -219,8 +244,9 @@ async function pathExists(path: string): Promise<boolean> {
}
/**
* Copied from https://github.com/GoogleChrome/chrome-launcher/blob/main/src/flags.ts
* with some flags commented out. Run tests after updating to compare.
* Copied from
* https://github.com/GoogleChrome/chrome-launcher/blob/main/src/flags.ts with
* some flags commented out. Run tests after updating to compare.
*/
const CHROME_LAUNCHER_DEFAULT_FLAGS = [
'--disable-features=' +
+2 -2
View File
@@ -4,8 +4,8 @@ import { beforeEach, describe, expect, expectTypeOf, it, vi } from 'vitest';
import { MigrationError, type WxtStorageItem, storage } from '../index';
/**
* This works because fakeBrowser is synchronous, and is will finish any number of chained
* calls within a single tick of the event loop, ie: a timeout of 0.
* This works because fakeBrowser is synchronous, and is will finish any number
* of chained calls within a single tick of the event loop, ie: a timeout of 0.
*/
async function waitForMigrations() {
return new Promise((res) => setTimeout(res));
+64 -100
View File
@@ -1,7 +1,9 @@
/**
* Simplified storage APIs with support for versioned fields, snapshots, metadata, and item definitions.
* Simplified storage APIs with support for versioned fields, snapshots,
* metadata, and item definitions.
*
* See [the guide](https://wxt.dev/storage.html) for more information.
*
* @module @wxt-dev/storage
*/
import { browser, type Browser } from '@wxt-dev/browser';
@@ -689,7 +691,7 @@ export interface WxtStorage {
* Get an item from storage, or return `null` if it doesn't exist.
*
* @example
* await storage.getItem<number>("local:installDate");
* await storage.getItem<number>('local:installDate');
*/
getItem<TValue>(
key: StorageItemKey,
@@ -702,11 +704,11 @@ export interface WxtStorage {
): Promise<TValue | null>;
/**
* Get multiple items from storage. The return order is guaranteed to be the same as the order
* requested.
* Get multiple items from storage. The return order is guaranteed to be the
* same as the order requested.
*
* @example
* await storage.getItems(["local:installDate", "session:someCounter"]);
* await storage.getItems(['local:installDate', 'session:someCounter']);
*/
getItems(
keys: Array<
@@ -717,11 +719,11 @@ export interface WxtStorage {
): Promise<Array<{ key: StorageItemKey; value: any }>>;
/**
* Return an object containing metadata about the key. Object is stored at `key + "$"`. If value
* is not an object, it returns an empty object.
* Return an object containing metadata about the key. Object is stored at
* `key + "$"`. If value is not an object, it returns an empty object.
*
* @example
* await storage.getMeta("local:installDate");
* await storage.getMeta('local:installDate');
*/
getMeta<T extends Record<string, unknown>>(key: StorageItemKey): Promise<T>;
@@ -736,22 +738,23 @@ export interface WxtStorage {
): Promise<Array<{ key: StorageItemKey; meta: any }>>;
/**
* Set a value in storage. Setting a value to `null` or `undefined` is equivalent to calling
* `removeItem`.
* Set a value in storage. Setting a value to `null` or `undefined` is
* equivalent to calling `removeItem`.
*
* @example
* await storage.setItem<number>("local:installDate", Date.now());
* await storage.setItem<number>('local:installDate', Date.now());
*/
setItem<T>(key: StorageItemKey, value: T | null): Promise<void>;
/**
* Set multiple values in storage. If a value is set to `null` or `undefined`, the key is removed.
* Set multiple values in storage. If a value is set to `null` or `undefined`,
* the key is removed.
*
* @example
* await storage.setItem([
* await storage.setItem([
* { key: "local:installDate", value: Date.now() },
* { key: "session:someCounter, value: 5 },
* ]);
* ]);
*/
setItems(
values: Array<
@@ -761,11 +764,11 @@ export interface WxtStorage {
): Promise<void>;
/**
* Sets metadata properties. If some properties are already set, but are not included in the
* `properties` parameter, they will not be removed.
* Sets metadata properties. If some properties are already set, but are not
* included in the `properties` parameter, they will not be removed.
*
* @example
* await storage.setMeta("local:installDate", { appVersion });
* await storage.setMeta('local:installDate', { appVersion });
*/
setMeta<T extends Record<string, unknown>>(
key: StorageItemKey,
@@ -788,13 +791,11 @@ export interface WxtStorage {
* Removes an item from storage.
*
* @example
* await storage.removeItem("local:installDate");
* await storage.removeItem('local:installDate');
*/
removeItem(key: StorageItemKey, opts?: RemoveItemOptions): Promise<void>;
/**
* Remove a list of keys from storage.
*/
/** Remove a list of keys from storage. */
removeItems(
keys: Array<
| StorageItemKey
@@ -804,48 +805,41 @@ export interface WxtStorage {
>,
): Promise<void>;
/**
* Removes all items from the provided storage area.
*/
/** Removes all items from the provided storage area. */
clear(base: StorageArea): Promise<void>;
/**
* Remove the entire metadata for a key, or specific properties by name.
*
* @example
* // Remove all metadata properties from the item
* await storage.removeMeta("local:installDate");
* // Remove all metadata properties from the item
* await storage.removeMeta('local:installDate');
*
* // Remove only specific the "v" field
* await storage.removeMeta("local:installDate", "v")
* // Remove only specific the "v" field
* await storage.removeMeta('local:installDate', 'v');
*/
removeMeta(
key: StorageItemKey,
properties?: string | string[],
): Promise<void>;
/**
* Return all the items in storage.
*/
/** Return all the items in storage. */
snapshot(
base: StorageArea,
opts?: SnapshotOptions,
): Promise<Record<string, unknown>>;
/**
* Restores the results of `snapshot`. If new properties have been saved since the snapshot, they are
* not overridden. Only values existing in the snapshot are overridden.
* Restores the results of `snapshot`. If new properties have been saved since
* the snapshot, they are not overridden. Only values existing in the snapshot
* are overridden.
*/
restoreSnapshot(base: StorageArea, data: any): Promise<void>;
/**
* Watch for changes to a specific key in storage.
*/
/** Watch for changes to a specific key in storage. */
watch<T>(key: StorageItemKey, cb: WatchCallback<T | null>): Unwatch;
/**
* Remove all watch listeners.
*/
/** Remove all watch listeners. */
unwatch(): void;
/**
@@ -894,61 +888,42 @@ export interface WxtStorageItem<
TValue,
TMetadata extends Record<string, unknown>,
> {
/**
* The storage key passed when creating the storage item.
*/
/** The storage key passed when creating the storage item. */
key: StorageItemKey;
/**
* @deprecated Renamed to fallback, use it instead.
*/
/** @deprecated Renamed to fallback, use it instead. */
defaultValue: TValue;
/**
* The value provided by the `fallback` option.
*/
/** The value provided by the `fallback` option. */
fallback: TValue;
/**
* Get the latest value from storage.
*/
/** Get the latest value from storage. */
getValue(): Promise<TValue>;
/**
* Get metadata.
*/
/** Get metadata. */
getMeta(): Promise<NullablePartial<TMetadata>>;
/**
* Set the value in storage.
*/
/** Set the value in storage. */
setValue(value: TValue): Promise<void>;
/**
* Set metadata properties.
*/
/** Set metadata properties. */
setMeta(properties: NullablePartial<TMetadata>): Promise<void>;
/**
* Remove the value from storage.
*/
/** Remove the value from storage. */
removeValue(opts?: RemoveItemOptions): Promise<void>;
/**
* Remove all metadata or certain properties from metadata.
*/
/** Remove all metadata or certain properties from metadata. */
removeMeta(properties?: string[]): Promise<void>;
/**
* Listen for changes to the value in storage.
*/
/** Listen for changes to the value in storage. */
watch(cb: WatchCallback<TValue>): Unwatch;
/**
* If there are migrations defined on the storage item, migrate to the latest version.
* If there are migrations defined on the storage item, migrate to the latest
* version.
*
* **This function is ran automatically whenever the extension updates**, so you don't have to call it
* manually.
* **This function is ran automatically whenever the extension updates**, so
* you don't have to call it manually.
*/
migrate(): Promise<void>;
}
@@ -957,14 +932,10 @@ export type StorageArea = 'local' | 'session' | 'sync' | 'managed';
export type StorageItemKey = `${StorageArea}:${string}`;
export interface GetItemOptions<T> {
/**
* @deprecated Renamed to `fallback`, use it instead.
*/
/** @deprecated Renamed to `fallback`, use it instead. */
defaultValue?: T;
/**
* Default value returned when `getItem` would otherwise return `null`.
*/
/** Default value returned when `getItem` would otherwise return `null`. */
fallback?: T;
}
@@ -979,21 +950,17 @@ export interface RemoveItemOptions {
export interface SnapshotOptions {
/**
* Exclude a list of keys. The storage area prefix should be removed since the snapshot is for a
* specific storage area already.
* Exclude a list of keys. The storage area prefix should be removed since the
* snapshot is for a specific storage area already.
*/
excludeKeys?: string[];
}
export interface WxtStorageItemOptions<T> {
/**
* @deprecated Renamed to `fallback`, use it instead.
*/
/** @deprecated Renamed to `fallback`, use it instead. */
defaultValue?: T;
/**
* Default value returned when `getValue` would otherwise return `null`.
*/
/** Default value returned when `getValue` would otherwise return `null`. */
fallback?: T;
/**
@@ -1005,25 +972,26 @@ export interface WxtStorageItemOptions<T> {
init?: () => T | Promise<T>;
/**
* Provide a version number for the storage item to enable migrations. When changing the version
* in the future, migration functions will be ran on application startup.
* Provide a version number for the storage item to enable migrations. When
* changing the version in the future, migration functions will be ran on
* application startup.
*/
version?: number;
/**
* A map of version numbers to the functions used to migrate the data to that version.
* A map of version numbers to the functions used to migrate the data to that
* version.
*/
migrations?: Record<number, (oldValue: any) => any>;
/**
* Print debug logs, such as migration process.
*
* @default false
*/
debug?: boolean;
/**
* A callback function that runs on migration complete.
*/
/** A callback function that runs on migration complete. */
onMigrationComplete?: (migratedValue: T, targetVersion: number) => void;
}
@@ -1032,21 +1000,17 @@ export type StorageAreaChanges = {
};
/**
* Same as `Partial`, but includes `| null`. It makes all the properties of an object optional and
* nullable.
* Same as `Partial`, but includes `| null`. It makes all the properties of an
* object optional and nullable.
*/
type NullablePartial<T> = {
[key in keyof T]+?: T[key] | undefined | null;
};
/**
* Callback called when a value in storage is changed.
*/
/** Callback called when a value in storage is changed. */
export type WatchCallback<T> = (newValue: T, oldValue: T) => void;
/**
* Call to remove a watch listener
*/
/** Call to remove a watch listener */
export type Unwatch = () => void;
export class MigrationError extends Error {
+10 -6
View File
@@ -38,12 +38,11 @@ export default defineWxtModule<UnoCSSOptions>({
},
});
/**
* Options for the UnoCSS module
*/
/** Options for the UnoCSS module */
export interface UnoCSSOptions<Theme extends object = object> {
/**
* Enable UnoCSS
*
* @default true
*/
enabled?: boolean;
@@ -51,12 +50,17 @@ export interface UnoCSSOptions<Theme extends object = object> {
* List of entrypoint names that UnoCSS is not used in. By default, the UnoCSS
* vite plugin is added to all build steps, but this option is used to exclude
* it from specific builds.
* @example ["popup", "options"]
* @default []
*
* @example
* {undefined} ('popup',
* 'options');
*
* @default [ ]
*/
excludeEntrypoints?: string[];
/**
* The path to your `unocss.config.ts` file, relative to <rootDir>, or inline configuration.
* The path to your `unocss.config.ts` file, relative to <rootDir>, or inline
* configuration.
*/
configOrPath?: Parameters<typeof UnoCSS<Theme>>[0];
}
+4 -3
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env node
/**
* A alias around `publish-extension` that is always installed on the path without having to install
* `publish-browser-extension` as a direct dependency (like for PNPM, which doesn't link
* sub-dependency binaries to "node_modules/.bin")
* A alias around `publish-extension` that is always installed on the path
* without having to install `publish-browser-extension` as a direct dependency
* (like for PNPM, which doesn't link sub-dependency binaries to
* "node_modules/.bin")
*/
await import('publish-browser-extension/cli');
+10 -14
View File
@@ -59,9 +59,7 @@ export class TestProject {
]);
}
/**
* Add a `wxt.config.ts` to the project with specific contents.
*/
/** Add a `wxt.config.ts` to the project with specific contents. */
setConfigFileConfig(config: UserConfig = {}) {
this.config = config;
this.files.push([
@@ -109,9 +107,7 @@ export class TestProject {
return server;
}
/**
* Call `path.resolve` relative to the project's root directory.
*/
/** Call `path.resolve` relative to the project's root directory. */
resolvePath(...path: string[]): string {
return resolve(this.root, ...path);
}
@@ -141,11 +137,11 @@ export class TestProject {
}
/**
* Read all the files from the test project's `.output` directory and combine them into a string
* that can be used in a snapshot.
* Read all the files from the test project's `.output` directory and combine
* them into a string that can be used in a snapshot.
*
* Optionally, provide a list of filenames whose content is not printed (because it's inconsistent
* or not relevant to a test).
* Optionally, provide a list of filenames whose content is not printed
* (because it's inconsistent or not relevant to a test).
*/
serializeOutput(ignoreContentsOfFilenames?: string[]): Promise<string> {
return this.serializeDir('.output', ignoreContentsOfFilenames);
@@ -154,8 +150,8 @@ export class TestProject {
/**
* Deeply print the filename and contents of all files in a directory.
*
* Optionally, provide a list of filenames whose content is not printed (because it's inconsistent
* or not relevant to a test).
* Optionally, provide a list of filenames whose content is not printed
* (because it's inconsistent or not relevant to a test).
*/
private async serializeDir(
dir: string,
@@ -179,8 +175,8 @@ export class TestProject {
/**
* @param path An absolute path to a file or a path relative to the root.
* @param ignoreContents An optional boolean that, when true, causes this function to not print
* the file contents.
* @param ignoreContents An optional boolean that, when true, causes this
* function to not print the file contents.
*/
async serializeFile(path: string, ignoreContents?: boolean): Promise<string> {
const absolutePath = this.resolvePath(path);
+12 -7
View File
@@ -1,25 +1,32 @@
/**
* Contains the `browser` export which you should use to access the extension APIs in your project:
* Contains the `browser` export which you should use to access the extension
* APIs in your project:
*
* ```ts
* import { browser } from 'wxt/browser';
*
* browser.runtime.onInstalled.addListener(() => {
* // ...
* })
* });
* ```
*
* @module wxt/browser
*/
import { browser as _browser, type Browser } from '@wxt-dev/browser';
import type { ScriptPublicPath } from './utils/inject-script';
/**
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
* This interface is empty because it is generated per-project when running `wxt
* prepare`. See:
*
* - `.wxt/types/paths.d.ts`
*/
export interface WxtRuntime {}
/**
* This interface is empty because it is generated per-project when running `wxt prepare`. See:
* This interface is empty because it is generated per-project when running `wxt
* prepare`. See:
*
* - `.wxt/types/i18n.d.ts`
*/
export interface WxtI18n {}
@@ -36,9 +43,7 @@ type InjectionResult<Result> = Array<
export interface WxtScripting {
executeScript: {
/**
* @see {@link Browser.scripting.executeScript}
*/
/** @see {@link Browser.scripting.executeScript} */
<Args extends any[], Result>(
injection: ScriptInjection<Args, Result>,
): Promise<InjectionResult<Result>>;
+4 -3
View File
@@ -8,7 +8,8 @@ import { registerWxt } from '../core/wxt';
import spawn from 'nano-spawn';
/**
* Wrap an action handler to add a timer, error handling, and maybe enable debug mode.
* Wrap an action handler to add a timer, error handling, and maybe enable debug
* mode.
*/
export function wrapAction(
cb: (
@@ -54,8 +55,8 @@ export function wrapAction(
}
/**
* Array flags, when not passed, are either `undefined` or `[undefined]`. This function filters out
* the
* Array flags, when not passed, are either `undefined` or `[undefined]`. This
* function filters out the
*/
export function getArrayFromFlags<T>(
flags: any,
+9 -8
View File
@@ -3,17 +3,18 @@ import { internalBuild } from './utils/building';
import { registerWxt } from './wxt';
/**
* Bundles the extension for production. Returns a promise of the build result. Discovers the `wxt.config.ts` file in
* the root directory, and merges that config with what is passed in.
* Bundles the extension for production. Returns a promise of the build result.
* Discovers the `wxt.config.ts` file in the root directory, and merges that
* config with what is passed in.
*
* @example
* // Use config from `wxt.config.ts`
* const res = await build()
* // Use config from `wxt.config.ts`
* const res = await build();
*
* // or override config `from wxt.config.ts`
* const res = await build({
* // Override config...
* })
* // or override config `from wxt.config.ts`
* const res = await build({
* // Override config...
* });
*/
export async function build(config?: InlineConfig): Promise<BuildOutput> {
await registerWxt('build', config);
+20 -16
View File
@@ -37,7 +37,8 @@ export async function createViteBuilder(
const vite = await import('vite');
/**
* Returns the base vite config shared by all builds based on the inline and user config.
* Returns the base vite config shared by all builds based on the inline and
* user config.
*/
const getBaseConfig = async (baseConfigOptions?: {
excludeAnalysisPlugin?: boolean;
@@ -102,7 +103,8 @@ export async function createViteBuilder(
};
/**
* Return the basic config for building an entrypoint in [lib mode](https://vitejs.dev/guide/build.html#library-mode).
* Return the basic config for building an entrypoint in [lib
* mode](https://vitejs.dev/guide/build.html#library-mode).
*/
const getLibModeConfig = (entrypoint: Entrypoint): vite.InlineConfig => {
const entry = getRollupEntry(entrypoint);
@@ -177,7 +179,8 @@ export async function createViteBuilder(
};
/**
* Return the basic config for building multiple entrypoints in [multi-page mode](https://vitejs.dev/guide/build.html#multi-page-app).
* Return the basic config for building multiple entrypoints in [multi-page
* mode](https://vitejs.dev/guide/build.html#multi-page-app).
*/
const getMultiPageConfig = (entrypoints: Entrypoint[]): vite.InlineConfig => {
const htmlEntrypoints = new Set(
@@ -210,7 +213,8 @@ export async function createViteBuilder(
};
/**
* Return the basic config for building a single CSS entrypoint in [multi-page mode](https://vitejs.dev/guide/build.html#multi-page-app).
* Return the basic config for building a single CSS entrypoint in [multi-page
* mode](https://vitejs.dev/guide/build.html#multi-page-app).
*/
const getCssConfig = (entrypoint: Entrypoint): vite.InlineConfig => {
return {
@@ -387,8 +391,8 @@ function getBuildOutputChunks(
}
/**
* Returns the input module ID (virtual or real file) for an entrypoint. The returned string should
* be passed as an input to rollup.
* Returns the input module ID (virtual or real file) for an entrypoint. The
* returned string should be passed as an input to rollup.
*/
function getRollupEntry(entrypoint: Entrypoint): string {
let virtualEntrypointType: VirtualEntrypointType | undefined;
@@ -413,15 +417,17 @@ function getRollupEntry(entrypoint: Entrypoint): string {
}
/**
* Ensures the HTML files output by a multipage build are in the correct location. This does two
* things:
* Ensures the HTML files output by a multipage build are in the correct
* location. This does two things:
*
* 1. Moves the HTML files to their final location at `<outDir>/<entrypoint.name>.html`.
* 2. Updates the bundle so it summarizes the files correctly in the returned build output.
* 1. Moves the HTML files to their final location at
* `<outDir>/<entrypoint.name>.html`.
* 2. Updates the bundle so it summarizes the files correctly in the returned build
* output.
*
* Assets (JS and CSS) are output to the `<outDir>/assets` directory, and don't need to be modified.
* HTML files access them via absolute URLs, so we don't need to update any import paths in the HTML
* files either.
* Assets (JS and CSS) are output to the `<outDir>/assets` directory, and don't
* need to be modified. HTML files access them via absolute URLs, so we don't
* need to update any import paths in the HTML files either.
*/
async function moveHtmlFiles(
config: ResolvedConfig,
@@ -465,9 +471,7 @@ async function moveHtmlFiles(
return movedChunks;
}
/**
* Recursively remove all directories that are empty/
*/
/** Recursively remove all directories that are empty/ */
export async function removeEmptyDirs(dir: string): Promise<void> {
const files = await fs.readdir(dir);
for (const file of files) {
@@ -15,9 +15,7 @@ export function bundleAnalysis(config: ResolvedConfig): vite.Plugin {
}) as vite.Plugin;
}
/**
* @internal FOR TESTING ONLY.
*/
/** @internal FOR TESTING ONLY. */
export function resetBundleIncrement() {
increment = 0;
}
@@ -3,14 +3,16 @@ 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
* placed in the correct place.
* Rename CSS entrypoint outputs to ensure a JS file is not generated, and that
* the CSS file is placed in the correct place.
*
* It:
*
* 1. Renames CSS files to their final paths
* 2. Removes the JS file that get's output by lib mode
*
* THIS PLUGIN SHOULD ONLY BE APPLIED TO CSS LIB MODE BUILDS. It should not be added to every build.
* THIS PLUGIN SHOULD ONLY BE APPLIED TO CSS LIB MODE BUILDS. It should not be
* added to every build.
*/
export function cssEntrypoints(
entrypoint: Entrypoint,
@@ -1,8 +1,8 @@
/**
* Overrides definitions for `import.meta.*`
*
* - `import.meta.url`: Without this, background service workers crash trying to access
* `document.location`, see https://github.com/wxt-dev/wxt/issues/392
* - `import.meta.url`: Without this, background service workers crash trying to
* access `document.location`, see https://github.com/wxt-dev/wxt/issues/392
*/
export function defineImportMeta() {
return {
@@ -10,7 +10,8 @@ import { hash } from 'ohash';
const inlineScriptContents: Record<string, string> = {};
/**
* Pre-renders the HTML entrypoints when building the extension to connect to the dev server.
* Pre-renders the HTML entrypoints when building the extension to connect to
* the dev server.
*/
export function devHtmlPrerender(
config: ResolvedConfig,
@@ -2,7 +2,8 @@ import type { Plugin } from 'vite';
import type { ResolvedConfig, WxtDevServer } from '../../../../types';
/**
* Defines global constants about the dev server. Helps scripts connect to the server's web socket.
* Defines global constants about the dev server. Helps scripts connect to the
* server's web socket.
*/
export function devServerGlobals(
config: ResolvedConfig,
@@ -3,11 +3,12 @@ import type { ResolvedConfig } from '../../../../types';
import { fetchCached } from '../../../utils/network';
/**
* Downloads any URL imports, like Google Analytics, into virtual modules so they are bundled with
* the extension instead of depending on remote code at runtime.
* Downloads any URL imports, like Google Analytics, into virtual modules so
* they are bundled with the extension instead of depending on remote code at
* runtime.
*
* @example
* import "url:https://google-tagmanager.com/gtag?id=XYZ";
* import 'url:https://google-tagmanager.com/gtag?id=XYZ';
*/
export function download(config: ResolvedConfig): Plugin {
return {
@@ -2,9 +2,7 @@ import type * as vite from 'vite';
import { EntrypointGroup } from '../../../../types';
import { getEntrypointGlobals } from '../../../utils/globals';
/**
* Define a set of global variables specific to an entrypoint.
*/
/** Define a set of global variables specific to an entrypoint. */
export function entrypointGroupGlobals(
entrypointGroup: EntrypointGroup,
): vite.PluginOption {
@@ -3,7 +3,8 @@ import type * as vite from 'vite';
import { ResolvedConfig } from '../../../../types';
/**
* Mock `wxt/browser` and stub the global `browser`/`chrome` types with a fake version of the extension APIs
* Mock `wxt/browser` and stub the global `browser`/`chrome` types with a fake
* version of the extension APIs
*/
export function extensionApiMock(config: ResolvedConfig): vite.PluginOption {
const virtualSetupModule = 'virtual:wxt-setup';
@@ -1,10 +1,11 @@
import type { Plugin } from 'vite';
/**
* Add a footer with the returned value so it can return values to `scripting.executeScript`
* Footer is added a part of esbuild to make sure it's not minified. It
* get's removed if added to `build.rollupOptions.output.footer`
* See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
* Add a footer with the returned value so it can return values to
* `scripting.executeScript` Footer is added a part of esbuild to make sure it's
* not minified. It get's removed if added to
* `build.rollupOptions.output.footer` See
* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
*/
export function iifeFooter(iifeReturnValueName: string): Plugin {
return {
@@ -2,8 +2,9 @@ import type { Plugin } from 'vite';
import { VIRTUAL_NOOP_BACKGROUND_MODULE_ID } from '../../../utils/constants';
/**
* In dev mode, if there's not a background script listed, we need to add one so that the web socket
* connection is setup and the extension reloads HTML pages and content scripts correctly.
* In dev mode, if there's not a background script listed, we need to add one so
* that the web socket connection is setup and the extension reloads HTML pages
* and content scripts correctly.
*/
export function noopBackground(): Plugin {
const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
@@ -5,7 +5,8 @@ import { removeMainFunctionCode } from '../../../utils/transform';
import { resolve } from 'node:path';
/**
* Transforms entrypoints, removing the main function from the entrypoint if it exists.
* Transforms entrypoints, removing the main function from the entrypoint if it
* exists.
*/
export function removeEntrypointMainFunction(
config: ResolvedConfig,
@@ -4,7 +4,8 @@ import type * as vite from 'vite';
import { ResolvedConfig } from '../../../../types';
/**
* When importing `virtual:app-config`, resolve it to the `app.config.ts` file in the project.
* When importing `virtual:app-config`, resolve it to the `app.config.ts` file
* in the project.
*/
export function resolveAppConfig(config: ResolvedConfig): vite.Plugin {
const virtualModuleId = 'virtual:app-config';
@@ -9,7 +9,8 @@ import fs from 'fs-extra';
import { resolve } from 'path';
/**
* Resolve all the virtual modules to the `node_modules/wxt/dist/virtual` directory.
* Resolve all the virtual modules to the `node_modules/wxt/dist/virtual`
* directory.
*/
export function resolveVirtualModules(config: ResolvedConfig): Plugin[] {
return virtualModuleNames.map((name) => {
@@ -4,7 +4,9 @@ import { normalizePath } from '../../../utils';
import { ResolvedConfig } from '../../../../types';
/**
* Resolve and load plugins for each entrypoint. This handles both JS entrypoints via the `virtual:wxt-plugins` import, and HTML files by adding `virtual:wxt-html-plugins` to the document's `<head>`
* Resolve and load plugins for each entrypoint. This handles both JS
* entrypoints via the `virtual:wxt-plugins` import, and HTML files by adding
* `virtual:wxt-html-plugins` to the document's `<head>`
*/
export function wxtPluginLoader(config: ResolvedConfig): vite.Plugin {
const virtualModuleId = 'virtual:wxt-plugins';
+7 -7
View File
@@ -8,21 +8,21 @@ import { registerWxt, wxt } from './wxt';
/**
* Remove generated/temp files from the directory.
*
* @param config Optional config that will override your `<root>/wxt.config.ts`.
*
* @example
* await clean();
* await clean();
*
* @param config Optional config that will override your `<root>/wxt.config.ts`.
*/
export async function clean(config?: InlineConfig): Promise<void>;
/**
* Remove generated/temp files from the directory.
*
* @deprecated
*
* @param root The directory to look for generated/temp files in. Defaults to `process.cwd()`. Can be relative to `process.cwd()` or absolute.
*
* @example
* await clean();
* await clean();
*
* @param root The directory to look for generated/temp files in. Defaults to
* `process.cwd()`. Can be relative to `process.cwd()` or absolute.
*/
export async function clean(root?: string): Promise<void>;
+11 -8
View File
@@ -32,13 +32,14 @@ import { createKeyboardShortcuts } from './keyboard-shortcuts';
import { isBabelSyntaxError, logBabelSyntaxError } from './utils/syntax-errors';
/**
* Creates a dev server and pre-builds all the files that need to exist before loading the extension.
* Creates a dev server and pre-builds all the files that need to exist before
* loading the extension.
*
* @example
* const server = await wxt.createServer({
* // Enter config...
* });
* await server.start();
* const server = await wxt.createServer({
* // Enter config...
* });
* await server.start();
*/
export async function createServer(
inlineConfig?: InlineConfig,
@@ -199,8 +200,8 @@ async function createServerInternal(): Promise<WxtDevServer> {
}
/**
* Returns a function responsible for reloading different parts of the extension when a file
* changes.
* Returns a function responsible for reloading different parts of the extension
* when a file changes.
*/
function createFileReloader(server: WxtDevServer) {
const fileChangedMutex = new Mutex();
@@ -295,7 +296,8 @@ function createFileReloader(server: WxtDevServer) {
}
/**
* From the server, tell the client to reload content scripts from the provided build step outputs.
* From the server, tell the client to reload content scripts from the provided
* build step outputs.
*/
function reloadContentScripts(steps: BuildStepOutput[], server: WxtDevServer) {
if (wxt.config.manifestVersion === 3) {
@@ -350,6 +352,7 @@ function getFilenameList(names: string[]): string {
/**
* Based on the current build output, return a list of files that are:
*
* 1. Not in node_modules
* 2. Not inside project root
*/
@@ -1,9 +1,7 @@
import consola from 'consola';
import { WebExtConfig } from '../types';
/**
* @deprecated Use `defineWebExtConfig` instead. Same function, different name.
*/
/** @deprecated Use `defineWebExtConfig` instead. Same function, different name. */
export function defineRunnerConfig(config: WebExtConfig): WebExtConfig {
consola.warn(
'`defineRunnerConfig` is deprecated, use `defineWebExtConfig` instead. See https://wxt.dev/guide/resources/upgrading.html#v0-19-0-rarr-v0-20-0',
@@ -12,7 +10,8 @@ export function defineRunnerConfig(config: WebExtConfig): WebExtConfig {
}
/**
* Configure how [`web-ext`](https://github.com/mozilla/web-ext) starts the browser during development.
* Configure how [`web-ext`](https://github.com/mozilla/web-ext) starts the
* browser during development.
*/
export function defineWebExtConfig(config: WebExtConfig): WebExtConfig {
return config;
+2 -1
View File
@@ -10,7 +10,8 @@ import { writeFileIfDifferent, getPublicFiles } from './utils/fs';
import { wxt } from './wxt';
/**
* Generate and write all the files inside the `InternalConfig.typesDir` directory.
* Generate and write all the files inside the `InternalConfig.typesDir`
* directory.
*/
export async function generateWxtDir(entrypoints: Entrypoint[]): Promise<void> {
await fs.ensureDir(wxt.config.typesDir);
+2 -6
View File
@@ -87,13 +87,9 @@ export async function initialize(options: {
}
interface Template {
/**
* Template's name.
*/
/** Template's name. */
name: string;
/**
* Path to template directory in github repo.
*/
/** Path to template directory in github repo. */
path: string;
}
+1 -3
View File
@@ -9,9 +9,7 @@ export interface KeyboardShortcutWatcher {
printHelp(flags: { canReopenBrowser: boolean }): void;
}
/**
* Function that creates a keyboard shortcut handler for the extension.
*/
/** Function that creates a keyboard shortcut handler for the extension. */
export function createKeyboardShortcuts(
server: WxtDevServer,
): KeyboardShortcutWatcher {
+9 -11
View File
@@ -31,11 +31,12 @@ import { getPort } from 'get-port-please';
import { fileURLToPath, pathToFileURL } from 'node:url';
/**
* Given an inline config, discover the config file if necessary, merge the results, resolve any
* relative paths, and apply any defaults.
* Given an inline config, discover the config file if necessary, merge the
* results, resolve any relative paths, and apply any defaults.
*
* Inline config always has priority over user config. Cli flags are passed as inline config if set.
* If unset, undefined is passed in, letting this function decide default values.
* Inline config always has priority over user config. Cli flags are passed as
* inline config if set. If unset, undefined is passed in, letting this function
* decide default values.
*/
export async function resolveConfig(
inlineConfig: InlineConfig,
@@ -249,7 +250,8 @@ async function resolveManifestConfig(
}
/**
* Merge the inline config and user config. Inline config is given priority. Defaults are not applied here.
* Merge the inline config and user config. Inline config is given priority.
* Defaults are not applied here.
*/
async function mergeInlineConfig(
inlineConfig: InlineConfig,
@@ -528,9 +530,7 @@ async function getUnimportEslintOptions(
};
}
/**
* Returns the path to `node_modules/wxt`.
*/
/** Returns the path to `node_modules/wxt`. */
function resolveWxtModuleDir() {
// TODO: Switch to import.meta.resolve() once the parent argument is unflagged
// (e.g. --experimental-import-meta-resolve) and all Node.js versions we support
@@ -554,9 +554,7 @@ function logMissingDir(logger: Logger, name: string, expected: string) {
);
}
/**
* Map of `ConfigEnv` commands to their default modes.
*/
/** Map of `ConfigEnv` commands to their default modes. */
const COMMAND_MODES: Record<WxtCommand, string> = {
build: 'production',
serve: 'development',
+1 -3
View File
@@ -2,9 +2,7 @@ import { ExtensionRunner } from '../../types';
import { relative } from 'node:path';
import { wxt } from '../wxt';
/**
* The manual runner tells the user to load the unpacked extension manually.
*/
/** The manual runner tells the user to load the unpacked extension manually. */
export function createManualRunner(): ExtensionRunner {
return {
async openBrowser() {
+2 -1
View File
@@ -3,7 +3,8 @@ import { relative } from 'node:path';
import { wxt } from '../wxt';
/**
* The Safari runner just logs a warning message because `web-ext` doesn't work with Safari.
* The Safari runner just logs a warning message because `web-ext` doesn't work
* with Safari.
*/
export function createSafariRunner(): ExtensionRunner {
return {
+1 -3
View File
@@ -4,9 +4,7 @@ import { formatDuration } from '../utils/time';
import defu from 'defu';
import { wxt } from '../wxt';
/**
* Create an `ExtensionRunner` backed by `web-ext`.
*/
/** Create an `ExtensionRunner` backed by `web-ext`. */
export function createWebExtRunner(): ExtensionRunner {
let runner: WebExtRunInstance | undefined;
+2 -1
View File
@@ -3,7 +3,8 @@ import { relative } from 'node:path';
import { wxt } from '../wxt';
/**
* The WSL runner just logs a warning message because `web-ext` doesn't work in WSL.
* The WSL runner just logs a warning message because `web-ext` doesn't work in
* WSL.
*/
export function createWslRunner(): ExtensionRunner {
return {
+3 -9
View File
@@ -1,6 +1,4 @@
/**
* Checks if `predicate` returns truthy for all elements of the array.
*/
/** Checks if `predicate` returns truthy for all elements of the array. */
export function every<T>(
array: T[],
predicate: (item: T, index: number) => boolean,
@@ -10,9 +8,7 @@ export function every<T>(
return true;
}
/**
* Returns true when any of the predicates return true;
*/
/** Returns true when any of the predicates return true; */
export function some<T>(
array: T[],
predicate: (item: T, index: number) => boolean,
@@ -22,9 +18,7 @@ export function some<T>(
return false;
}
/**
* Convert an item or array to an array.
*/
/** Convert an item or array to an array. */
export function toArray<T>(a: T | T[]): T[] {
return Array.isArray(a) ? a : [a];
}
@@ -9,27 +9,36 @@ import { normalizePath } from '../paths';
import { wxt } from '../../wxt';
/**
* Compare the changed files vs the build output and determine what kind of reload needs to happen:
* Compare the changed files vs the build output and determine what kind of
* reload needs to happen:
*
* - Do nothing
* - CSS or JS file associated with an HTML page is changed - this is handled automatically by the
* dev server
*
* - CSS or JS file associated with an HTML page is changed - this is handled
* automatically by the dev server
* - Change isn't used by any of the entrypoints
* - Reload Content script
*
* - CSS or JS file associated with a content script
* - Background script will be told to reload the content script
* - Reload HTML file
* - HTML file itself is saved - HMR doesn't handle this because the HTML pages are pre-rendered
* - Chrome is OK reloading the page when the HTML file is changed without reloading the whole
* extension. Not sure about firefox, this might need to change to an extension reload
*
* - HTML file itself is saved - HMR doesn't handle this because the HTML pages
* are pre-rendered
* - Chrome is OK reloading the page when the HTML file is changed without
* reloading the whole extension. Not sure about firefox, this might need
* to change to an extension reload
* - Reload extension
*
* - Background script is changed
* - Manifest is different
* - Restart browser
* - web-ext.config.ts (runner config changes)
*
* - Web-ext.config.ts (runner config changes)
* - Full dev server restart
* - wxt.config.ts (main config file)
* - modules/* (any file related to WXT modules)
*
* - Wxt.config.ts (main config file)
* - Modules/* (any file related to WXT modules)
* - .env (environment variable changed could effect build)
*/
export function detectDevChanges(
@@ -126,7 +135,8 @@ export function detectDevChanges(
}
/**
* For a single change, return all the step of the build output that were effected by it.
* For a single change, return all the step of the build output that were
* effected by it.
*/
function findEffectedSteps(
changedFile: string,
@@ -165,8 +175,8 @@ function findEffectedSteps(
}
/**
* Contains information about what files changed, what needs rebuilt, and the type of reload that is
* required.
* Contains information about what files changed, what needs rebuilt, and the
* type of reload that is required.
*/
export type DevModeChange =
| NoChange
@@ -181,13 +191,9 @@ interface NoChange {
}
interface RebuildChange {
/**
* The list of entrypoints that need rebuilt.
*/
/** The list of entrypoints that need rebuilt. */
rebuildGroups: EntrypointGroup[];
/**
* The previous output stripped of any files are going to change.
*/
/** The previous output stripped of any files are going to change. */
cachedOutput: BuildOutput;
}
@@ -30,7 +30,8 @@ import { wxt } from '../../wxt';
import { camelCase } from 'scule';
/**
* Return entrypoints and their configuration by looking through the project's files.
* Return entrypoints and their configuration by looking through the project's
* files.
*/
export async function findEntrypoints(): Promise<Entrypoint[]> {
// Make sure required TSConfig file exists to load dependencies
@@ -195,7 +196,10 @@ async function importEntrypoints(infos: EntrypointInfo[]) {
return resMap;
}
/** Extract `manifest.` and `wxt.` options from meta tags, converting snake_case keys to camelCase */
/**
* Extract `manifest.` and `wxt.` options from meta tags, converting snake_case
* keys to camelCase
*/
async function importHtmlEntrypoint(
info: EntrypointInfo,
): Promise<Record<string, any>> {
@@ -1,8 +1,8 @@
import { Entrypoint, EntrypointGroup } from '../../../types';
/**
* Entrypoints are built in groups. HTML pages can all be built together in a single step,
* content scripts must be build individually, etc.
* 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.
*/
@@ -20,13 +20,14 @@ import { mergeJsonOutputs } from '@aklinker1/rollup-plugin-visualizer';
import { isCI } from 'ci-info';
/**
* Builds the extension based on an internal config. No more config discovery is performed, the
* build is based on exactly what is passed in.
* Builds the extension based on an internal config. No more config discovery is
* performed, the build is based on exactly what is passed in.
*
* This function:
*
* 1. Cleans the output directory
* 2. Executes the rebuild function with a blank previous output so everything is built (see
* `rebuild` for more details)
* 2. Executes the rebuild function with a blank previous output so everything is
* built (see `rebuild` for more details)
* 3. Prints the summary
*/
export async function internalBuild(): Promise<BuildOutput> {
@@ -6,20 +6,22 @@ import { wxt } from '../../wxt';
import type { Browser } from '@wxt-dev/browser';
/**
* Given a configuration, list of entrypoints, and an existing, partial output, build the
* entrypoints and merge the new output with the existing output.
* Given a configuration, list of entrypoints, and an existing, partial output,
* build the entrypoints and merge the new output with the existing output.
*
* This function will:
*
* 1. Generate the .wxt directory's types
* 2. Build the `entrypointGroups` (and copies public files)
* 3. Generate the latest manifest for all entrypoints
* 4. Write the new manifest to the file system
*
* @param config Internal config containing all the project information.
* @param allEntrypoints List of entrypoints used to generate the types inside .wxt directory.
* @param allEntrypoints List of entrypoints used to generate the types inside
* .wxt directory.
* @param entrypointGroups The list of entrypoint groups to build.
* @param existingOutput The previous output to combine the rebuild results into. An emptry array if
* this is the first build.
* @param existingOutput The previous output to combine the rebuild results
* into. An emptry array if this is the first build.
*/
export async function rebuild(
allEntrypoints: Entrypoint[],
+2 -2
View File
@@ -4,8 +4,8 @@ import { dirname, resolve } from 'path';
import { writeFileIfDifferent } from './fs';
/**
* A basic file system cache stored at `<srcDir>/.wxt/cache/<key>`. Just caches a string in a
* file for the given key.
* A basic file system cache stored at `<srcDir>/.wxt/cache/<key>`. Just caches
* a string in a file for the given key.
*
* @param srcDir Absolute path to source directory. See `InternalConfig.srcDir`
*/
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* Module ID used to build the background in dev mode if the extension doesn't include a background
* script/service worker.
* Module ID used to build the background in dev mode if the extension doesn't
* include a background script/service worker.
*/
export const VIRTUAL_NOOP_BACKGROUND_MODULE_ID = 'virtual:user-background';
@@ -4,7 +4,8 @@ import { getEntrypointBundlePath } from './entrypoints';
import { ManifestContentScript } from './types';
/**
* Returns a unique and consistent string hash based on a content scripts options.
* Returns a unique and consistent string hash based on a content scripts
* options.
*
* It is able to recognize default values,
*/
@@ -1,5 +1,6 @@
/**
* Directive names that make up CSPs. There are more, this is all I need for the plugin.
* Directive names that make up CSPs. There are more, this is all I need for the
* plugin.
*/
export type CspDirective = 'default-src' | 'script-src' | 'object-src';
@@ -25,9 +26,7 @@ export class ContentSecurityPolicy {
}
}
/**
* Ensure a set of values are listed under a directive.
*/
/** Ensure a set of values are listed under a directive. */
add(directive: CspDirective, ...newValues: string[]): ContentSecurityPolicy {
const values = this.data[directive] ?? [];
newValues.forEach((newValue) => {
+5 -7
View File
@@ -27,8 +27,8 @@ export function getEntrypointOutputFile(
}
/**
* Return's the entrypoint's output path relative to the output directory. Used for paths in the
* manifest and rollup's bundle.
* Return's the entrypoint's output path relative to the output directory. Used
* for paths in the manifest and rollup's bundle.
*/
export function getEntrypointBundlePath(
entrypoint: Entrypoint,
@@ -40,9 +40,7 @@ export function getEntrypointBundlePath(
);
}
/**
* Given an entrypoint option, resolve it's value based on a target browser.
*/
/** Given an entrypoint option, resolve it's value based on a target browser. */
export function resolvePerBrowserOption<T>(
option: PerBrowserOption<T>,
browser: TargetBrowser,
@@ -55,8 +53,8 @@ export function resolvePerBrowserOption<T>(
/**
* Given an entrypoint option, resolve it's value based on a target browser.
*
* defaultIcon is special, it's the only key that's a record, which can confuse this function. So
* it's been manually excluded from resolution.
* DefaultIcon is special, it's the only key that's a record, which can confuse
* this function. So it's been manually excluded from resolution.
*/
export function resolvePerBrowserOptions<
T extends Record<string, any>,
+1 -3
View File
@@ -2,9 +2,7 @@ import { config } from 'dotenv';
import { expand } from 'dotenv-expand';
import type { TargetBrowser } from '../../types';
/**
* Load environment files based on the current mode and browser.
*/
/** Load environment files based on the current mode and browser. */
export function loadEnv(mode: string, browser: TargetBrowser) {
return expand(
config({
+5 -4
View File
@@ -4,8 +4,9 @@ import { unnormalizePath } from './paths';
import { wxt } from '../wxt';
/**
* Only write the contents to a file if it results in a change. This prevents unnecessary file
* watchers from being triggered, like WXT's dev server or the TS language server in editors.
* Only write the contents to a file if it results in a change. This prevents
* unnecessary file watchers from being triggered, like WXT's dev server or the
* TS language server in editors.
*
* @param file The file to write to.
* @param newContents The new text content to write.
@@ -24,8 +25,8 @@ export async function writeFileIfDifferent(
}
/**
* Get all the files in the project's public directory. Returned paths are relative to the
* `config.publicDir`.
* Get all the files in the project's public directory. Returned paths are
* relative to the `config.publicDir`.
*/
export async function getPublicFiles(): Promise<string[]> {
if (!(await fs.pathExists(wxt.config.publicDir))) return [];
+20 -26
View File
@@ -23,9 +23,7 @@ import { wxt } from '../wxt';
import { ManifestV3WebAccessibleResource } from './types';
import type { Browser } from '@wxt-dev/browser';
/**
* Writes the manifest to the output directory and the build output.
*/
/** Writes the manifest to the output directory and the build output. */
export async function writeManifest(
manifest: Browser.runtime.Manifest,
output: BuildOutput,
@@ -44,9 +42,7 @@ export async function writeManifest(
});
}
/**
* Generates the manifest based on the config and entrypoints.
*/
/** Generates the manifest based on the config and entrypoints. */
export async function generateManifest(
allEntrypoints: Entrypoint[],
buildOutput: Omit<BuildOutput, 'manifest'>,
@@ -155,8 +151,9 @@ export async function generateManifest(
}
/**
* Removes suffixes from the version, like X.Y.Z-alpha1 (which browsers don't allow), so it's a
* simple version number, like X or X.Y or X.Y.Z, which browsers allow.
* Removes suffixes from the version, like X.Y.Z-alpha1 (which browsers don't
* allow), so it's a simple version number, like X or X.Y or X.Y.Z, which
* browsers allow.
*/
function simplifyVersion(versionName: string): string {
// Regex adapted from here: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/version#version_format
@@ -514,8 +511,8 @@ function addDevModePermissions(manifest: Browser.runtime.Manifest) {
}
/**
* Returns the bundle paths to CSS files associated with a list of content scripts, or undefined if
* there is no associated CSS.
* Returns the bundle paths to CSS files associated with a list of content
* scripts, or undefined if there is no associated CSS.
*/
export function getContentScriptCssFiles(
contentScripts: ContentScriptEntrypoint[],
@@ -541,9 +538,9 @@ export function getContentScriptCssFiles(
}
/**
* Content scripts configured with `cssInjectionMode: "ui"` need to add their CSS files to web
* accessible resources so they can be fetched as text and added to shadow roots that the UI is
* added to.
* Content scripts configured with `cssInjectionMode: "ui"` need to add their
* CSS files to web accessible resources so they can be fetched as text and
* added to shadow roots that the UI is added to.
*/
export function getContentScriptCssWebAccessibleResources(
contentScripts: ContentScriptEntrypoint[],
@@ -571,8 +568,8 @@ export function getContentScriptCssWebAccessibleResources(
}
/**
* Based on the build output, return a Record of each content script's name to it CSS file if the
* script includes one.
* Based on the build output, return a Record of each content script's name to
* it CSS file if the script includes one.
*/
export function getContentScriptsCssMap(
buildOutput: Omit<BuildOutput, 'manifest'>,
@@ -610,8 +607,8 @@ function addHostPermission(
}
/**
* - "<all_urls>" &rarr; "<all_urls>"
* - "*://play.google.com/books/*" &rarr; "*://play.google.com/*"
* - "<all_urls>" "<all_urls>"
* - "_://play.google.com/books/_" "_://play.google.com/_"
*/
export function stripPathFromMatchPattern(pattern: string) {
const protocolSepIndex = pattern.indexOf('://');
@@ -622,9 +619,10 @@ export function stripPathFromMatchPattern(pattern: string) {
}
/**
* Converts all MV3 web accessible resources to their MV2 forms. MV3 web accessible resources are
* generated in this file, and may be defined by the user in their manifest. In both cases, when
* targeting MV2, automatically convert their definitions down to the basic MV2 array.
* Converts all MV3 web accessible resources to their MV2 forms. MV3 web
* accessible resources are generated in this file, and may be defined by the
* user in their manifest. In both cases, when targeting MV2, automatically
* convert their definitions down to the basic MV2 array.
*/
export function convertWebAccessibleResourcesToMv2(
manifest: Browser.runtime.Manifest,
@@ -674,9 +672,7 @@ function convertCspToMv2(manifest: Browser.runtime.Manifest): void {
manifest.content_security_policy.extension_pages;
}
/**
* Make sure all resources are in MV3 format. If not, add a warning.
*/
/** Make sure all resources are in MV3 format. If not, add a warning. */
function validateMv3WebAccessibleResources(
manifest: Browser.runtime.Manifest,
): void {
@@ -694,9 +690,7 @@ function validateMv3WebAccessibleResources(
}
}
/**
* Remove keys from the manifest based on the build target.
*/
/** Remove keys from the manifest based on the build target. */
function stripKeys(manifest: Browser.runtime.Manifest): void {
let keysToRemove: string[] = [];
if (wxt.config.manifestVersion === 2) {
@@ -8,10 +8,10 @@ import { minimatch, MinimatchOptions } from 'minimatch';
* matches any of the negative patterns, it will return `false`.
*
* @example
* ```ts
* minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
* minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
* ```
* ```ts
* minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
* minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
* ```;
*/
export function minimatchMultiple(
search: string,
+2 -2
View File
@@ -21,8 +21,8 @@ export async function isOnline(): Promise<boolean> {
}
/**
* Fetches a URL with a simple GET request. Grabs it from cache if it doesn't exist, or throws an
* error if it can't be resolved via the network or cache.
* Fetches a URL with a simple GET request. Grabs it from cache if it doesn't
* exist, or throws an error if it can't be resolved via the network or cache.
*/
export async function fetchCached(
url: string,
+4 -3
View File
@@ -2,15 +2,16 @@ import systemPath from 'node:path';
import normalize from 'normalize-path';
/**
* Converts system paths to normalized bundler path.
* On Windows, this returns paths with `/` instead of `\`.
* Converts system paths to normalized bundler path. On Windows, this returns
* paths with `/` instead of `\`.
*/
export function normalizePath(path: string): string {
return normalize(path);
}
/**
* Given a normalized path, convert it to the system path style. On Windows, switch to \, otherwise use /.
* Given a normalized path, convert it to the system path style. On Windows,
* switch to , otherwise use /.
*/
export function unnormalizePath(path: string): string {
return systemPath.normalize(path);
+5 -6
View File
@@ -7,9 +7,7 @@ export function kebabCaseAlphanumeric(str: string): string {
.replace(/\s+/g, '-'); // Replace spaces with hyphens
}
/**
* Return a safe variable name for a given string.
*/
/** Return a safe variable name for a given string. */
export function safeVarName(str: string): string {
const name = camelCase(kebabCaseAlphanumeric(str));
if (name.match(/^[a-z]/)) return name;
@@ -18,15 +16,16 @@ export function safeVarName(str: string): string {
}
/**
* Converts a string to a valid filename (NOT path), stripping out invalid characters.
* Converts a string to a valid filename (NOT path), stripping out invalid
* characters.
*/
export function safeFilename(str: string): string {
return kebabCaseAlphanumeric(str);
}
/**
* Removes import statements from the top of a file. Keeps import.meta and inline, async `import()`
* calls.
* Removes import statements from the top of a file. Keeps import.meta and
* inline, async `import()` calls.
*/
export function removeImportStatements(text: string): string {
return text.replace(
@@ -1,6 +1,4 @@
/**
* SHOULD ONLY BE IMPORTED IN TESTS.
*/
/** SHOULD ONLY BE IMPORTED IN TESTS. */
import { resolve } from 'path';
import { faker } from '@faker-js/faker';
import merge from 'lodash.merge';
+3 -4
View File
@@ -5,9 +5,7 @@ export function formatDuration(duration: number): string {
return `${(duration / 1e3).toFixed(0)} s`;
}
/**
* Add a timeout to a promise.
*/
/** Add a timeout to a promise. */
export function withTimeout<T>(
promise: Promise<T>,
duration: number,
@@ -24,7 +22,8 @@ export function withTimeout<T>(
}
/**
* @deprecated Don't use in production, just for testing and slowing things down.
* @deprecated Don't use in production, just for testing and slowing things
* down.
*/
export function sleep(ms: number): Promise<void> {
return new Promise((res) => setTimeout(res, ms));
+7 -3
View File
@@ -2,10 +2,13 @@ import { ProxifiedModule, parseModule } from 'magicast';
/**
* Removes any code used at runtime related to an entrypoint's main function.
*
* 1. Removes or clears out `main` function from returned object
* 2. Removes any unused functions/variables outside the definition that aren't being called/used
* 2. Removes any unused functions/variables outside the definition that aren't
* being called/used
* 3. Removes unused imports
* 3. Removes value-less, side-effect only imports (like `import "./styles.css"` or `import "polyfill"`)
* 4. Removes value-less, side-effect only imports (like `import "./styles.css"` or
* `import "polyfill"`)
*/
export function removeMainFunctionCode(code: string): {
code: string;
@@ -216,7 +219,8 @@ function removeSideEffectImports(mod: ProxifiedModule): void {
}
/**
* Util to get the AST as a simple JSON object, stripping out large objects and file locations to keep it readible
* Util to get the AST as a simple JSON object, stripping out large objects and
* file locations to keep it readible
*/
function getSimpleAstJson(ast: any): any {
if (!ast) {
+2 -2
View File
@@ -4,8 +4,8 @@ import type { Browser } from '@wxt-dev/browser';
* Remove optional from key, but keep undefined if present
*
* @example
* type Test = NullablyRequired<{a?: string, b: number}>
* // type Test = {a: string | undefined, b: number}
* type Test = NullablyRequired<{ a?: string; b: number }>;
* // type Test = {a: string | undefined, b: number}
*/
export type NullablyRequired<T> = { [K in keyof Required<T>]: T[K] };
@@ -7,19 +7,22 @@ export const virtualEntrypointTypes = [
export type VirtualEntrypointType = (typeof virtualEntrypointTypes)[0];
/**
* All the names of entrypoint files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
* All the names of entrypoint files in the `src/virtual/` and `dist/virtual/`
* directories, minus the extension.
*/
export const virtualEntrypointModuleNames = virtualEntrypointTypes.map(
(name) => `${name}-entrypoint` as const,
);
/**
* Name of entrypoint files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
* Name of entrypoint files in the `src/virtual/` and `dist/virtual/`
* directories, minus the extension.
*/
export type VirtualEntrypointModuleName =
(typeof virtualEntrypointModuleNames)[0];
/**
* All the names of files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
* All the names of files in the `src/virtual/` and `dist/virtual/` directories,
* minus the extension.
*/
export const virtualModuleNames = [
...virtualEntrypointModuleNames,
@@ -27,11 +30,10 @@ export const virtualModuleNames = [
'reload-html' as const,
];
/**
* Name of files in the `src/virtual/` and `dist/virtual/` directories, minus the extension.
* Name of files in the `src/virtual/` and `dist/virtual/` directories, minus
* the extension.
*/
export type VirtualModuleName = (typeof virtualModuleNames)[0];
/**
* Import alias used for importing a virtual module
*/
/** Import alias used for importing a virtual module */
export type VirtualModuleId = `virtual:wxt-${VirtualModuleName}`;
+1 -3
View File
@@ -1,9 +1,7 @@
// TODO: Someone smarter than me should just mock this module instead.
import isWsl_ from 'is-wsl';
/**
* Returns true when running on WSL or WSL2.
*/
/** Returns true when running on WSL or WSL2. */
export function isWsl(): boolean {
return isWsl_;
}
+11 -11
View File
@@ -7,13 +7,15 @@ import { builtinModules } from '../builtin-modules';
import { relative } from 'path';
/**
* Global variable set once `createWxt` is called once. Since this variable is used everywhere, this
* global can be used instead of passing the variable as a function parameter everywhere.
* Global variable set once `createWxt` is called once. Since this variable is
* used everywhere, this global can be used instead of passing the variable as a
* function parameter everywhere.
*/
export let wxt: Wxt;
/**
* Create and register a global instance of the Wxt interface for use throughout the project.
* Create and register a global instance of the Wxt interface for use throughout
* the project.
*/
export async function registerWxt(
command: WxtCommand,
@@ -94,20 +96,18 @@ async function initWxtModule(module: WxtModule<any>): Promise<void> {
);
}
/**
* Unloads WXT modules.
*/
/** Unloads WXT modules. */
export function deinitWxtModules(): void {
wxt.hooks.removeAllHooks();
}
/**
* @internal ONLY USE FOR TESTING.
*
* @example
* setWxtForTesting(fakeWxt({ ... }));
* // Or use the shorthand
* setFakeWxt({ ... })
* setWxtForTesting(fakeWxt({ ... }));
* // Or use the shorthand
* setFakeWxt({ ... })
*
* @internal ONLY USE FOR TESTING.
*/
export function setWxtForTesting(testInstance: Wxt) {
wxt = testInstance;
+1
View File
@@ -14,6 +14,7 @@ import { minimatchMultiple } from './utils/minimatch-multiple';
/**
* Build and zip the extension for distribution.
*
* @param config Optional config that will override your `<root>/wxt.config.ts`.
* @returns A list of all files included in the ZIP.
*/
+1
View File
@@ -1,5 +1,6 @@
/**
* This module contains:
*
* - JS APIs used by the CLI to build extensions or start dev mode.
* - Helper functions for defining project config.
* - Types for building and extension or configuring WXT.
+67 -63
View File
@@ -1,5 +1,6 @@
/**
* Utilities for creating [WXT Modules](https://wxt.dev/guide/essentials/wxt-modules.html).
* Utilities for creating [WXT
* Modules](https://wxt.dev/guide/essentials/wxt-modules.html).
*
* @module wxt/modules
*/
@@ -37,20 +38,20 @@ export function defineWxtModule<TOptions extends WxtModuleOptions>(
* To extract entrypoint options from a JS/TS file, use
* `wxt.builder.importEntrypoint` (see example).
*
* @example
* export default defineWxtModule(async (wxt, options) => {
* const entrypointPath = '/path/to/my-entrypoint.ts';
* addEntrypoint(wxt, {
* type: 'content-script',
* name: 'some-name',
* inputPath: entrypointPath,
* outputDir: wxt.config.outDir,
* options: await wxt.builder.importEntrypoint(entrypointPath),
* });
* });
*
* @param wxt The wxt instance provided by the module's setup function.
* @param entrypoint The entrypoint to be bundled along with the extension.
*
* @example
* export default defineWxtModule(async (wxt, options) => {
* const entrypointPath = "/path/to/my-entrypoint.ts";
* addEntrypoint(wxt, {
* type: "content-script",
* name: "some-name",
* inputPath: entrypointPath,
* outputDir: wxt.config.outDir,
* options: await wxt.builder.importEntrypoint(entrypointPath),
* });
* });
*/
export function addEntrypoint(wxt: Wxt, entrypoint: Entrypoint): void {
wxt.hooks.hook('entrypoints:resolved', (_, entrypoints) => {
@@ -63,13 +64,13 @@ export function addEntrypoint(wxt: Wxt, entrypoint: Entrypoint): void {
* extension's output directory. The directory itself is not copied, just the
* files inside it. If a filename matches an existing one, it is ignored.
*
* @example
* export default defineWxtModule((wxt, options) => {
* addPublicAssets(wxt, './dist/prebundled');
* });
*
* @param wxt The wxt instance provided by the module's setup function.
* @param dir The directory to copy.
*
* @example
* export default defineWxtModule((wxt, options) => {
* addPublicAssets(wxt, "./dist/prebundled");
* });
*/
export function addPublicAssets(wxt: Wxt, dir: string): void {
wxt.hooks.hook('build:publicAssets', async (wxt, files) => {
@@ -85,22 +86,22 @@ export function addPublicAssets(wxt: Wxt, dir: string): void {
}
/**
* Merge additional vite config for one or more entrypoint "groups" that make
* up individual builds. Config in the project's `wxt.config.ts` file takes
* Merge additional vite config for one or more entrypoint "groups" that make up
* individual builds. Config in the project's `wxt.config.ts` file takes
* precedence over any config added by this function.
*
* @example
* export default defineWxtModule((wxt, options) => {
* addViteConfig(wxt, () => ({
* build: {
* sourceMaps: true,
* },
* });
* });
*
* @param wxt The wxt instance provided by the module's setup function.
* @param viteConfig A function that returns the vite config the module is
adding. Same format as `vite` in `wxt.config.ts`.
*
* @example
* export default defineWxtModule((wxt, options) => {
* addViteConfig(wxt, () => ({
* build: {
* sourceMaps: true,
* },
* });
* });
* adding. Same format as `vite` in `wxt.config.ts`.
*/
export function addViteConfig(
wxt: Wxt,
@@ -120,17 +121,17 @@ export function addViteConfig(
}
/**
* Add a runtime plugin to the project. In each entrypoint, before executing
* the `main` function, plugins are executed.
* Add a runtime plugin to the project. In each entrypoint, before executing the
* `main` function, plugins are executed.
*
* @example
* export default defineWxtModule((wxt) => {
* addWxtPlugin(wxt, 'wxt-module-analytics/client-plugin');
* });
*
* @param wxt The wxt instance provided by the module's setup function.
* @param plugin An import from an NPM module, or an absolute file path to the
* file to load at runtime.
*
* @example
* export default defineWxtModule((wxt) => {
* addWxtPlugin(wxt, "wxt-module-analytics/client-plugin");
* });
* file to load at runtime.
*/
export function addWxtPlugin(wxt: Wxt, plugin: string): void {
wxt.hooks.hook('config:resolved', (wxt) => {
@@ -139,31 +140,34 @@ export function addWxtPlugin(wxt: Wxt, plugin: string): void {
}
/**
* Add an Unimport preset ([built-in](https://github.com/unjs/unimport?tab=readme-ov-file#built-in-presets),
* Add an Unimport preset
* ([built-in](https://github.com/unjs/unimport?tab=readme-ov-file#built-in-presets),
* [custom](https://github.com/unjs/unimport?tab=readme-ov-file#custom-presets),
* or [auto-scanned](https://github.com/unjs/unimport?tab=readme-ov-file#exports-auto-scan)),
* or
* [auto-scanned](https://github.com/unjs/unimport?tab=readme-ov-file#exports-auto-scan)),
* to the project's list of auto-imported utilities.
*
* Some things to note:
* - This function will only de-duplicate built-in preset names. It will not
* stop you adding duplicate custom or auto-scanned presets.
*
* - This function will only de-duplicate built-in preset names. It will not stop
* you adding duplicate custom or auto-scanned presets.
* - If the project has disabled imports, this function has no effect.
*
* @param wxt The wxt instance provided by the module's setup function.
* @param preset The preset to add to the project.
*
* @example
* export default defineWxtModule((wxt) => {
* export default defineWxtModule((wxt) => {
* // Built-in preset:
* addImportPreset(wxt, "vue");
* // Custom preset:
* addImportPreset(wxt, {
* from: "vue",
* imports: ["ref", "reactive", ...],
* from: "vue",
* imports: ["ref", "reactive", ...],
* });
* // Auto-scanned preset:
* addImportPreset(wxt, { package: "vue" });
* });
* });
*
* @param wxt The wxt instance provided by the module's setup function.
* @param preset The preset to add to the project.
*/
export function addImportPreset(
wxt: Wxt,
@@ -179,8 +183,8 @@ export function addImportPreset(
}
/**
* Adds an import alias to the project's TSConfig paths and bundler. Path can
* be absolute or relative to the project's root directory.
* Adds an import alias to the project's TSConfig paths and bundler. Path can be
* absolute or relative to the project's root directory.
*
* Usually, this is used to provide access to some code generated by your
* module. In the example below, a `i18n` plugin generates a variable that it
@@ -188,22 +192,22 @@ export function addImportPreset(
* to it.
*
* @example
* import path from 'node:path';
* import path from 'node:path';
*
* export default defineWxtModule((wxt) => {
* const i18nPath = path.resolve(wxt.config.wxtDir, "i18n.ts");
* export default defineWxtModule((wxt) => {
* const i18nPath = path.resolve(wxt.config.wxtDir, 'i18n.ts');
*
* // Generate the file
* wxt.hooks.hook("prepare:types", (_, entries) => {
* entries.push({
* path: i18nPath,
* text: `export const i18n = ...`,
* // Generate the file
* wxt.hooks.hook('prepare:types', (_, entries) => {
* entries.push({
* path: i18nPath,
* text: `export const i18n = ...`,
* });
* });
* });
*
* // Add alias
* addAlias(wxt, "#i18n", i18nPath);
* });
* // Add alias
* addAlias(wxt, '#i18n', i18nPath);
* });
*/
export function addAlias(wxt: Wxt, alias: string, path: string) {
wxt.hooks.hook('config:resolved', (wxt) => {
+4 -3
View File
@@ -1,9 +1,10 @@
/**
* The fake browser is automatically used as a mock for the `wxt/browser` import
* when using `wxt/testing/vitest-plugin` with Vitest. It is also setup to
* reset all state before each test.
* when using `wxt/testing/vitest-plugin` with Vitest. It is also setup to reset
* all state before each test.
*
* This module is just a re-export of [@webext-core/fake-browser](https://webext-core.aklinker1.io/fake-browser/triggering-events).
* This module is just a re-export of
* [@webext-core/fake-browser](https://webext-core.aklinker1.io/fake-browser/triggering-events).
*
* @module wxt/testing/fake-browser
*/
+2 -3
View File
@@ -1,10 +1,9 @@
/**
* Utilities for unit testing WXT extensions.
*
* @deprecated Use `wxt/testing/*` instead to prevent issues with JSDOM or
* HappyDOM environments. Will be removed in the next major version of WXT.
*
* @module wxt/testing
* @deprecated Use `wxt/testing/*` instead to prevent issues with JSDOM or
* HappyDOM environments. Will be removed in the next major version of WXT.
*/
export * from './fake-browser';
export * from './wxt-vitest-plugin';
@@ -18,7 +18,8 @@ import UnimportPlugin from 'unimport/unplugin';
import { registerWxt, wxt } from '../core/wxt';
/**
* Vite plugin that configures Vitest with everything required to test a WXT extension, based on the `<root>/wxt.config.ts`
* Vite plugin that configures Vitest with everything required to test a WXT
* extension, based on the `<root>/wxt.config.ts`
*
* ```ts
* // vitest.config.ts
@@ -30,7 +31,8 @@ import { registerWxt, wxt } from '../core/wxt';
* });
* ```
*
* @param inlineConfig Customize WXT's config for testing. Any config specified here overrides the config from your `wxt.config.ts` file.
* @param inlineConfig Customize WXT's config for testing. Any config specified
* here overrides the config from your `wxt.config.ts` file.
*/
export async function WxtVitest(
inlineConfig?: InlineConfig,
+453 -430
View File
File diff suppressed because it is too large Load Diff
@@ -9,20 +9,22 @@ import {
import { createLocationWatcher } from './internal/location-watcher';
/**
* Implements [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
* Implements
* [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
* Used to detect and stop content script code when the script is invalidated.
*
* It also provides several utilities like `ctx.setTimeout` and `ctx.setInterval` that should be used in
* content scripts instead of `window.setTimeout` or `window.setInterval`.
* It also provides several utilities like `ctx.setTimeout` and
* `ctx.setInterval` that should be used in content scripts instead of
* `window.setTimeout` or `window.setInterval`.
*
* To create context for testing, you can use the class's constructor:
*
* ```ts
* import { ContentScriptContext } from 'wxt/utils/content-scripts-context';
*
* test("storage listener should be removed when context is invalidated", () => {
* test('storage listener should be removed when context is invalidated', () => {
* const ctx = new ContentScriptContext('test');
* const item = storage.defineItem("local:count", { defaultValue: 0 });
* const item = storage.defineItem('local:count', { defaultValue: 0 });
* const watcher = vi.fn();
*
* const unwatch = item.watch(watcher);
@@ -78,17 +80,18 @@ export class ContentScriptContext implements AbortController {
}
/**
* Add a listener that is called when the content script's context is invalidated.
*
* @returns A function to remove the listener.
* Add a listener that is called when the content script's context is
* invalidated.
*
* @example
* browser.runtime.onMessage.addListener(cb);
* const removeInvalidatedListener = ctx.onInvalidated(() => {
* browser.runtime.onMessage.removeListener(cb);
* })
* // ...
* removeInvalidatedListener();
* browser.runtime.onMessage.addListener(cb);
* const removeInvalidatedListener = ctx.onInvalidated(() => {
* browser.runtime.onMessage.removeListener(cb);
* });
* // ...
* removeInvalidatedListener();
*
* @returns A function to remove the listener.
*/
onInvalidated(cb: () => void): () => void {
this.signal.addEventListener('abort', cb);
@@ -96,15 +99,15 @@ export class ContentScriptContext implements AbortController {
}
/**
* Return a promise that never resolves. Useful if you have an async function that shouldn't run
* after the context is expired.
* Return a promise that never resolves. Useful if you have an async function
* that shouldn't run after the context is expired.
*
* @example
* const getValueFromStorage = async () => {
* if (ctx.isInvalid) return ctx.block();
* const getValueFromStorage = async () => {
* if (ctx.isInvalid) return ctx.block();
*
* // ...
* }
* // ...
* };
*/
block<T>(): Promise<T> {
return new Promise(() => {
@@ -113,7 +116,8 @@ export class ContentScriptContext implements AbortController {
}
/**
* Wrapper around `window.setInterval` that automatically clears the interval when invalidated.
* Wrapper around `window.setInterval` that automatically clears the interval
* when invalidated.
*
* Intervals can be cleared by calling the normal `clearInterval` function.
*/
@@ -126,7 +130,8 @@ export class ContentScriptContext implements AbortController {
}
/**
* Wrapper around `window.setTimeout` that automatically clears the interval when invalidated.
* Wrapper around `window.setTimeout` that automatically clears the interval
* when invalidated.
*
* Timeouts can be cleared by calling the normal `setTimeout` function.
*/
@@ -139,10 +144,11 @@ export class ContentScriptContext implements AbortController {
}
/**
* Wrapper around `window.requestAnimationFrame` that automatically cancels the request when
* invalidated.
* Wrapper around `window.requestAnimationFrame` that automatically cancels
* the request when invalidated.
*
* Callbacks can be canceled by calling the normal `cancelAnimationFrame` function.
* Callbacks can be canceled by calling the normal `cancelAnimationFrame`
* function.
*/
requestAnimationFrame(callback: FrameRequestCallback): number {
const id = requestAnimationFrame((...args) => {
@@ -154,10 +160,11 @@ export class ContentScriptContext implements AbortController {
}
/**
* Wrapper around `window.requestIdleCallback` that automatically cancels the request when
* invalidated.
* Wrapper around `window.requestIdleCallback` that automatically cancels the
* request when invalidated.
*
* Callbacks can be canceled by calling the normal `cancelIdleCallback` function.
* Callbacks can be canceled by calling the normal `cancelIdleCallback`
* function.
*/
requestIdleCallback(
callback: IdleRequestCallback,
@@ -172,23 +179,26 @@ export class ContentScriptContext implements AbortController {
}
/**
* Call `target.addEventListener` and remove the event listener when the context is invalidated.
* Call `target.addEventListener` and remove the event listener when the
* context is invalidated.
*
* Listeners can be canceled by calling the normal `removeEventListener` function.
* Listeners can be canceled by calling the normal `removeEventListener`
* function.
*
* Includes additional events useful for content scripts:
*
* - `"wxt:locationchange"` - Triggered when HTML5 history mode is used to change URL. Content
* scripts are not reloaded when navigating this way, so this can be used to reset the content
* script state on URL change, or run custom code.
* - `"wxt:locationchange"` - Triggered when HTML5 history mode is used to
* change URL. Content scripts are not reloaded when navigating this way, so
* this can be used to reset the content script state on URL change, or run
* custom code.
*
* @example
* ctx.addEventListener(document, "visibilitychange", () => {
* // ...
* });
* ctx.addEventListener(window, "wxt:locationchange", () => {
* // ...
* });
* ctx.addEventListener(document, 'visibilitychange', () => {
* // ...
* });
* ctx.addEventListener(window, 'wxt:locationchange', () => {
* // ...
* });
*/
addEventListener<TType extends keyof WxtWindowEventMap>(
target: Window,
@@ -6,9 +6,7 @@ import { createShadowRootUi } from '../shadow-root';
import { ContentScriptContext } from '../../content-script-context';
import { ContentScriptUi } from '../types';
/**
* Util for floating promise.
*/
/** Util for floating promise. */
async function runMicrotasks() {
return await new Promise((resolve) => setTimeout(resolve, 0));
}
@@ -489,9 +487,7 @@ describe('Content Script UIs', () => {
});
});
/**
* Need call runMicrotasks after floating-promise and append/remove dom
*/
/** Need call runMicrotasks after floating-promise and append/remove dom */
describe('auto mount', () => {
const DYNAMIC_CHILD_ID = 'dynamic-child';
let ui: ContentScriptUi<any>;
@@ -49,34 +49,33 @@ export function createIframeUi<TMounted>(
export interface IframeContentScriptUi<
TMounted,
> extends ContentScriptUi<TMounted> {
/**
* The iframe added to the DOM.
*/
/** The iframe added to the DOM. */
iframe: HTMLIFrameElement;
/**
* A wrapper div that assists in positioning.
*/
/** A wrapper div that assists in positioning. */
wrapper: HTMLDivElement;
}
export type IframeContentScriptUiOptions<TMounted> =
ContentScriptUiOptions<TMounted> & {
/**
* The path to the HTML page that will be shown in the iframe. This string is passed into
* `browser.runtime.getURL`.
* The path to the HTML page that will be shown in the iframe. This string
* is passed into `browser.runtime.getURL`.
*/
// @ts-expect-error: HtmlPublicPath is generated per-project
page: import('wxt/browser').HtmlPublicPath;
/**
* Callback executed when mounting the UI. Use this function to customize the iframe or wrapper
* element's appearance. It is called every time `ui.mount()` is called.
* Callback executed when mounting the UI. Use this function to customize
* the iframe or wrapper element's appearance. It is called every time
* `ui.mount()` is called.
*
* Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback.
* Optionally return a value that can be accessed at `ui.mounted` or in the
* `onRemove` callback.
*/
onMount?: (wrapper: HTMLElement, iframe: HTMLIFrameElement) => TMounted;
/**
* Callback executed before mounting the UI. Use this function to customize the iframe or wrapper
* elements before they are injected into the DOM. It is called every time `ui.mount()` is called.
* Callback executed before mounting the UI. Use this function to customize
* the iframe or wrapper elements before they are injected into the DOM. It
* is called every time `ui.mount()` is called.
*/
onBeforeMount?: (wrapper: HTMLElement, iframe: HTMLIFrameElement) => void;
};
@@ -48,14 +48,13 @@ export function createIntegratedUi<TMounted>(
/**
* Shared types for the different `wxt/utils/content-script-ui/*` modules.
*
* @module wxt/utils/content-script-ui/types
*/
export interface IntegratedContentScriptUi<
TMounted,
> extends ContentScriptUi<TMounted> {
/**
* A wrapper div that assists in positioning.
*/
/** A wrapper div that assists in positioning. */
wrapper: HTMLElement;
}
@@ -64,14 +63,16 @@ export type IntegratedContentScriptUiOptions<TMounted> =
/**
* Tag used to create the wrapper element.
*
* @default "div"
* @default 'div'
*/
tag?: string;
/**
* Callback executed when mounting the UI. This function should create and append the UI to the
* `wrapper` element. It is called every time `ui.mount()` is called.
* Callback executed when mounting the UI. This function should create and
* append the UI to the `wrapper` element. It is called every time
* `ui.mount()` is called.
*
* Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback.
* Optionally return a value that can be accessed at `ui.mounted` or in the
* `onRemove` callback.
*/
onMount: (wrapper: HTMLElement) => TMounted;
};
@@ -8,7 +8,8 @@ import { logger } from '../internal/logger';
import { splitShadowRootCss } from '../split-shadow-root-css';
/**
* Create a content script UI inside a [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot).
* Create a content script UI inside a
* [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot).
*
* > This function is async because it has to load the CSS via a network call.
*
@@ -115,9 +116,7 @@ export async function createShadowRootUi<TMounted>(
};
}
/**
* Load the CSS for the current entrypoint.
*/
/** Load the CSS for the current entrypoint. */
async function loadCss(): Promise<string> {
const url = browser.runtime
// @ts-expect-error: getURL is defined per-project, but not inside the package
@@ -138,69 +137,80 @@ export interface ShadowRootContentScriptUi<
TMounted,
> extends ContentScriptUi<TMounted> {
/**
* The `HTMLElement` hosting the shadow root used to isolate the UI's styles. This is the element
* that get's added to the DOM. This element's style is not isolated from the webpage.
* The `HTMLElement` hosting the shadow root used to isolate the UI's styles.
* This is the element that get's added to the DOM. This element's style is
* not isolated from the webpage.
*/
shadowHost: HTMLElement;
/**
* The container element inside the `ShadowRoot` whose styles are isolated. The UI is mounted
* inside this `HTMLElement`.
* The container element inside the `ShadowRoot` whose styles are isolated.
* The UI is mounted inside this `HTMLElement`.
*/
uiContainer: HTMLElement;
/**
* The shadow root performing the isolation.
*/
/** The shadow root performing the isolation. */
shadow: ShadowRoot;
}
export type ShadowRootContentScriptUiOptions<TMounted> =
ContentScriptUiOptions<TMounted> & {
/**
* The name of the custom component used to host the ShadowRoot. Must be kebab-case.
* The name of the custom component used to host the ShadowRoot. Must be
* kebab-case.
*/
name: string;
/**
* Custom CSS text to apply to the UI. If your content script imports/generates CSS and you've
* set `cssInjectionMode: "ui"`, the imported CSS will be included automatically. You do not need
* to pass those styles in here. This is for any additional styles not in the imported CSS.
* Custom CSS text to apply to the UI. If your content script
* imports/generates CSS and you've set `cssInjectionMode: "ui"`, the
* imported CSS will be included automatically. You do not need to pass
* those styles in here. This is for any additional styles not in the
* imported CSS.
*/
css?: string;
/**
* ShadowRoot's mode.
*
* @default 'open'
* @see https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/mode
* @default "open"
*/
mode?: 'open' | 'closed';
/**
* When enabled, `event.stopPropagation` will be called on events trying to bubble out of the
* shadow root.
* When enabled, `event.stopPropagation` will be called on events trying to
* bubble out of the shadow root.
*
* - Set to `true` to stop the propagation of a default set of events,
* `["keyup", "keydown", "keypress"]`
* - Set to an array of event names to stop the propagation of a custom list of events
* - Set to an array of event names to stop the propagation of a custom list
* of events
*/
isolateEvents?: boolean | string[];
/**
* By default, WXT adds `all: initial` to the shadow root before the rest of
* your CSS. This resets any inheritable CSS styles that
* [normally pierce the Shadow DOM](https://open-wc.org/guides/knowledge/styling/styles-piercing-shadow-dom/).
* your CSS. This resets any inheritable CSS styles that [normally pierce
* the Shadow
* DOM](https://open-wc.org/guides/knowledge/styling/styles-piercing-shadow-dom/).
*
* WXT resets everything but:
* - **`rem` Units**: they continue to scale based off the webpage's HTML `font-size`.
* - **CSS Variables/Custom Properties**: CSS variables defined outside the shadow root can be accessed inside it.
* - **`@font-face` Definitions**: Fonts defined outside the shadow root can be used inside it.
*
* To disable this behavior and inherit styles from the webpage, set `inheritStyles: true`.
* - **`rem` Units**: they continue to scale based off the webpage's HTML
* `font-size`.
* - **CSS Variables/Custom Properties**: CSS variables defined outside the
* shadow root can be accessed inside it.
* - **`@font-face` Definitions**: Fonts defined outside the shadow root can
* be used inside it.
*
* To disable this behavior and inherit styles from the webpage, set
* `inheritStyles: true`.
*
* @default false
*/
inheritStyles?: boolean;
/**
* Callback executed when mounting the UI. This function should create and append the UI to the
* `uiContainer` element. It is called every time `ui.mount()` is called.
* Callback executed when mounting the UI. This function should create and
* append the UI to the `uiContainer` element. It is called every time
* `ui.mount()` is called.
*
* Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback.
* Optionally return a value that can be accessed at `ui.mounted` or in the
* `onRemove` callback.
*/
onMount: (
uiContainer: HTMLElement,
@@ -7,11 +7,12 @@ export interface ContentScriptUi<TMounted> extends MountFunctions {
export type ContentScriptUiOptions<TMounted> = ContentScriptPositioningOptions &
ContentScriptAnchoredOptions & {
/**
* Callback called before the UI is removed from the webpage. Use to cleanup your UI, like
* unmounting your Vue or React apps.
* Callback called before the UI is removed from the webpage. Use to cleanup
* your UI, like unmounting your Vue or React apps.
*
* Note that this callback is called only when `ui.remove` is called - that means it is
* not called automatically when the anchor is removed, unless you use `autoMount`.
* Note that this callback is called only when `ui.remove` is called - that
* means it is not called automatically when the anchor is removed, unless
* you use `autoMount`.
*/
onRemove?: (mounted: TMounted | undefined) => void;
};
@@ -23,7 +24,8 @@ export type ContentScriptOverlayAlignment =
| 'bottom-right';
/**
* ![Visualization of different append modes](https://wxt.dev/content-script-ui-append.png)
* [Visualization of different append
* modes](https://wxt.dev/content-script-ui-append.png)
*/
export type ContentScriptAppendMode =
| 'last'
@@ -40,17 +42,18 @@ export interface ContentScriptInlinePositioningOptions {
export interface ContentScriptOverlayPositioningOptions {
position: 'overlay';
/**
* The `z-index` used on the `wrapper` element. Set to a positive number to show your UI over website
* content.
* The `z-index` used on the `wrapper` element. Set to a positive number to
* show your UI over website content.
*/
zIndex?: number;
/**
* When using `type: "overlay"`, the mounted element is 0px by 0px in size. Alignment specifies
* which corner is aligned with that 0x0 pixel space.
* When using `type: "overlay"`, the mounted element is 0px by 0px in size.
* Alignment specifies which corner is aligned with that 0x0 pixel space.
*
* ![Visualization of alignment options](https://wxt.dev/content-script-ui-alignment.png)
* [Visualization of alignment
* options](https://wxt.dev/content-script-ui-alignment.png)
*
* @default "top-left"
* @default 'top-left'
*/
alignment?: ContentScriptOverlayAlignment;
}
@@ -58,8 +61,8 @@ export interface ContentScriptOverlayPositioningOptions {
export interface ContentScriptModalPositioningOptions {
position: 'modal';
/**
* The `z-index` used on the `shadowHost`. Set to a positive number to show your UI over website
* content.
* The `z-index` used on the `shadowHost`. Set to a positive number to show
* your UI over website content.
*/
zIndex?: number;
}
@@ -67,7 +70,8 @@ export interface ContentScriptModalPositioningOptions {
/**
* Choose between `"inline"`, `"overlay"`, or `"modal"` positions.
*
* ![Visualization of different types](https://wxt.dev/content-script-ui-position.png)
* [Visualization of different
* types](https://wxt.dev/content-script-ui-position.png)
*/
export type ContentScriptPositioningOptions =
| ContentScriptInlinePositioningOptions
@@ -76,8 +80,9 @@ export type ContentScriptPositioningOptions =
export interface ContentScriptAnchoredOptions {
/**
* 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.
* 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?:
| string
@@ -93,44 +98,36 @@ export interface ContentScriptAnchoredOptions {
* - `"replace"` - Replace the `anchor` element with the UI.
* - `"before"` - Add the UI as the sibling before the `anchor` element
* - `"after"` - Add the UI as the sibling after the `anchor` element
* - `(anchor, ui) => void` - Customizable function that let's you add the UI to the DOM
* - `(anchor, ui) => void` - Customizable function that let's you add the UI to
* the DOM
*/
append?: ContentScriptAppendMode | ((anchor: Element, ui: Element) => void);
}
export interface BaseMountFunctions {
/**
* Function that mounts or remounts the UI on the page.
*/
/** Function that mounts or remounts the UI on the page. */
mount: () => void;
/**
* Function that removes the UI from the webpage.
*/
/** Function that removes the UI from the webpage. */
remove: () => void;
}
export interface MountFunctions extends BaseMountFunctions {
/**
* Call `ui.autoMount()` to automatically mount and remove the UI as the anchor is dynamically added/removed by the webpage.
* Call `ui.autoMount()` to automatically mount and remove the UI as the
* anchor is dynamically added/removed by the webpage.
*/
autoMount: (options?: AutoMountOptions) => void;
}
export type AutoMountOptions = {
/**
* When true, only mount and unmount a UI once.
*/
/** When true, only mount and unmount a UI once. */
once?: boolean;
/**
* The callback triggered when `StopAutoMount` is called.
*/
/** The callback triggered when `StopAutoMount` is called. */
onStop?: () => void;
};
export type StopAutoMount = () => void;
export interface AutoMount {
/**
* Stop watching the anchor element for changes, but keep the UI mounted.
*/
/** Stop watching the anchor element for changes, but keep the UI mounted. */
stopAutoMount: StopAutoMount;
}
+4 -3
View File
@@ -4,15 +4,16 @@ export interface WxtAppConfig {}
/**
* Runtime app config defined in `<srcDir>/app.config.ts`.
*
* You can add fields to this interface via ["Module Augmentation"](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation):
* You can add fields to this interface via ["Module
* Augmentation"](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation):
*
* ```ts
* // app.config.ts
* import 'wxt/utils/define-app-config';
*
* declare module "wxt/utils/define-app-config" {
* declare module 'wxt/utils/define-app-config' {
* export interface WxtAppConfig {
* analytics: AnalyticsConfig
* analytics: AnalyticsConfig;
* }
* }
* ```
@@ -12,7 +12,8 @@ export class WxtLocationChangeEvent extends Event {
}
/**
* Returns an event name unique to the extension and content script that's running.
* Returns an event name unique to the extension and content script that's
* running.
*/
export function getUniqueEventName(eventName: string): string {
return `${browser?.runtime?.id}:${import.meta.env.ENTRYPOINT}:${eventName}`;
@@ -27,9 +27,7 @@ export interface WxtWebSocket extends WebSocket {
let ws: WxtWebSocket | undefined;
/**
* Connect to the websocket and listen for messages.
*/
/** Connect to the websocket and listen for messages. */
export function getDevServerWebSocket(): WxtWebSocket {
if (import.meta.env.COMMAND !== 'serve')
throw Error(
@@ -5,9 +5,9 @@ const supportsNavigationApi =
typeof (globalThis as any).navigation?.addEventListener === 'function';
/**
* Create a util that watches for URL changes, dispatching the custom event when detected. Stops
* watching when content script is invalidated. Uses Navigation API when available, otherwise
* falls back to polling.
* Create a util that watches for URL changes, dispatching the custom event when
* detected. Stops watching when content script is invalidated. Uses Navigation
* API when available, otherwise falls back to polling.
*/
export function createLocationWatcher(ctx: ContentScriptContext) {
let lastUrl: URL;
@@ -15,8 +15,8 @@ export function createLocationWatcher(ctx: ContentScriptContext) {
return {
/**
* Ensure the location watcher is actively looking for URL changes. If it's already watching,
* this is a noop.
* Ensure the location watcher is actively looking for URL changes. If it's
* already watching, this is a noop.
*/
run() {
if (watching) return;
+1 -3
View File
@@ -11,9 +11,7 @@ function print(method: (...args: any[]) => void, ...args: any[]) {
}
}
/**
* Wrapper around `console` with a "[wxt]" prefix
*/
/** Wrapper around `console` with a "[wxt]" prefix */
export const logger = {
debug: (...args: any[]) => print(console.debug, ...args),
log: (...args: any[]) => print(console.log, ...args),
+3 -1
View File
@@ -1,5 +1,7 @@
/**
* Re-export the [`@webext-core/match-patterns` package](https://www.npmjs.com/package/@webext-core/match-patterns).
* Re-export the [`@webext-core/match-patterns`
* package](https://www.npmjs.com/package/@webext-core/match-patterns).
*
* @module wxt/utils/match-patterns
*/
export * from '@webext-core/match-patterns';
@@ -3,9 +3,13 @@
const AT_RULE_BLOCKS = /(\s*@(property|font-face)[\s\S]*?{[\s\S]*?})/gm;
/**
* Given a CSS string that will be loaded into a shadow root, split it into two parts:
* - `documentCss`: CSS that needs to be applied to the document (like `@property`)
* Given a CSS string that will be loaded into a shadow root, split it into two
* parts:
*
* - `documentCss`: CSS that needs to be applied to the document (like
* `@property`)
* - `shadowCss`: CSS that needs to be applied to the shadow root
*
* @param css
*/
export function splitShadowRootCss(css: string): {
+3 -1
View File
@@ -1,5 +1,7 @@
/**
* Re-export the [`@wxt-dev/storage` package](https://www.npmjs.com/package/@wxt-dev/storage).
* Re-export the [`@wxt-dev/storage`
* package](https://www.npmjs.com/package/@wxt-dev/storage).
*
* @module wxt/utils/storage
*/
export * from '@wxt-dev/storage';
@@ -1,8 +1,6 @@
import { browser } from 'wxt/browser';
/**
* https://developer.chrome.com/blog/longer-esw-lifetimes/
*/
/** https://developer.chrome.com/blog/longer-esw-lifetimes/ */
export function keepServiceWorkerAlive() {
setInterval(async () => {
// Calling an async browser API resets the service worker's timeout
+29
View File
@@ -67,6 +67,9 @@ importers:
prettier:
specifier: ^3.8.1
version: 3.8.1
prettier-plugin-jsdoc:
specifier: ^1.8.0
version: 1.8.0(prettier@3.8.1)
semver:
specifier: ^7.7.4
version: 7.7.4
@@ -2394,6 +2397,9 @@ packages:
resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==}
hasBin: true
binary-searching@2.0.5:
resolution: {integrity: sha512-v4N2l3RxL+m4zDxyxz3Ne2aTmiPn8ZUpKFpdPtO+ItW1NcTCXA7JeHG5GMBSvoKSkQZ9ycS+EouDVxYB9ufKWA==}
birpc@2.9.0:
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
@@ -2564,6 +2570,10 @@ packages:
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
engines: {node: ^12.20.0 || >=14}
comment-parser@1.4.5:
resolution: {integrity: sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==}
engines: {node: '>= 12.0.0'}
compare-func@2.0.0:
resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
@@ -3915,6 +3925,12 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
prettier-plugin-jsdoc@1.8.0:
resolution: {integrity: sha512-byW8EBZ1DSA3CPdDGBXfcdqqhh2eq0+HlIOPTGZ6rf9O2p/AwBmtS0e49ot5ZeOdcszj81FyzbyHr/VS0eYpCg==}
engines: {node: '>=14.13.1 || >=16.0.0'}
peerDependencies:
prettier: ^3.0.0
prettier@3.8.1:
resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==}
engines: {node: '>=14'}
@@ -6521,6 +6537,8 @@ snapshots:
baseline-browser-mapping@2.9.19: {}
binary-searching@2.0.5: {}
birpc@2.9.0: {}
birpc@4.0.0: {}
@@ -6697,6 +6715,8 @@ snapshots:
commander@9.5.0: {}
comment-parser@1.4.5: {}
compare-func@2.0.0:
dependencies:
array-ify: 1.0.0
@@ -8215,6 +8235,15 @@ snapshots:
prelude-ls@1.2.1: {}
prettier-plugin-jsdoc@1.8.0(prettier@3.8.1):
dependencies:
binary-searching: 2.0.5
comment-parser: 1.4.5
mdast-util-from-markdown: 2.0.2
prettier: 3.8.1
transitivePeerDependencies:
- supports-color
prettier@3.8.1: {}
pretty-bytes@7.1.0: {}