Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dd9bc7772 | |||
| c7335add19 | |||
| c133498208 | |||
| 6a52bb22a3 | |||
| 6f970efda5 | |||
| c69350a0eb | |||
| 4add8820eb | |||
| 033dd4811e | |||
| da5cd32502 | |||
| edf33fdec9 | |||
| 448cbf16c9 | |||
| e45b77b528 | |||
| b61fd1716e | |||
| d584b5cc7e | |||
| faf9cedfbd | |||
| 3a38ac9339 | |||
| 7ea36a3cfc | |||
| 3577c0b47a | |||
| d0bef42186 | |||
| d9f10c62ad | |||
| 35d3c03e4b | |||
| fdd38a1580 | |||
| 3018801f06 | |||
| 8e96bfef06 | |||
| 31071bd11e | |||
| f5619f6be1 | |||
| adad1b5a2c | |||
| a6c4e19a5d | |||
| af6f74c344 | |||
| ce45cb8a89 | |||
| 14d4aa202d | |||
| 4c35798dba | |||
| 656a9b365f | |||
| 67fa3db921 | |||
| f2d3061e97 | |||
| 760c34e416 | |||
| 48398b315c | |||
| 45d0d9d7f1 | |||
| b0f4ac8221 |
@@ -71,6 +71,9 @@ const chromeExtensionIds = [
|
||||
'bmoggiinmnodjphdjnmpcnlleamkfedj', // AliasVault - Open-Source Password & (Email) Alias Manager
|
||||
'hlnhhamckimoaiekbglafiebkfimhapb', // SnapThePrice: AI-Powered Real-time Lowest Price Finder
|
||||
'gdjampjdgjmbifnhldgcnccdjkcoicmg', // radiofrance - news & broadcasts (French), music (international)
|
||||
'jlnhphlghikichhgbnkepenehbmloenb', // Blens - Time Tracker and AI Insight
|
||||
'njnammmpdodmfkodnfpammnpdcbhnlcm', // Always Light Mode - Setting website always in light mode
|
||||
'lblmfclcfniabobmamfkdogcgdagbhhb', // DesignPicker - Color Picker & Font Detector
|
||||
];
|
||||
|
||||
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
|
||||
|
||||
@@ -8,6 +8,16 @@ outline: deep
|
||||
|
||||
During development, WXT uses [`web-ext` by Mozilla](https://www.npmjs.com/package/web-ext) to automatically open a browser window with your extension installed.
|
||||
|
||||
:::danger
|
||||
Chrome 137 removed support for the `--load-extension` CLI flag, which WXT relied on to open the browser with an extension installed. So this feature will not work for Chrome.
|
||||
|
||||
You have two options:
|
||||
|
||||
1. Install [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (which still supports the `--load-extension` flag) and [point the `chrome` binary to it](#set-browser-binaries), or
|
||||
2. [Disable this feature](#disable-opening-browser) and manually load your extension
|
||||
|
||||
:::
|
||||
|
||||
## Config Files
|
||||
|
||||
You can configure browser startup in 3 places:
|
||||
|
||||
@@ -211,12 +211,12 @@ When defining your background entrypoint, keep in mind that WXT will import this
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
browser.action.onClick.addListener(() => { // [!code --]
|
||||
browser.action.onClicked.addListener(() => { // [!code --]
|
||||
// ... // [!code --]
|
||||
}); // [!code --]
|
||||
|
||||
export default defineBackground(() => {
|
||||
browser.action.onClick.addListener(() => { // [!code ++]
|
||||
browser.action.onClicked.addListener(() => { // [!code ++]
|
||||
// ... // [!code ++]
|
||||
}); // [!code ++]
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ import { injectScript, createShadowRootUi } from '#imports';
|
||||
But Vitest sees this:
|
||||
|
||||
```ts
|
||||
import { injectScript } from 'wxt/browser';
|
||||
import { injectScript } from 'wxt/utils/inject-script';
|
||||
import { createShadowRootUi } from 'wxt/utils/content-script-ui/shadow-root';
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"engines": {
|
||||
"node": ">=18.20.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.8.0",
|
||||
"packageManager": "pnpm@10.10.0",
|
||||
"scripts": {
|
||||
"check": "check && pnpm -r --sequential run check",
|
||||
"test": "pnpm -r --sequential run test run",
|
||||
@@ -17,33 +17,33 @@
|
||||
"docs:preview": "pnpm -s docs:gen && vitepress preview docs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/buildc": "catalog:",
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@commitlint/config-conventional": "catalog:",
|
||||
"@commitlint/types": "catalog:",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"changelogen": "catalog:",
|
||||
"consola": "catalog:",
|
||||
"fast-glob": "catalog:",
|
||||
"feed": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"markdown-it-footnote": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"simple-git-hooks": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typedoc": "catalog:",
|
||||
"typedoc-plugin-frontmatter": "catalog:",
|
||||
"typedoc-plugin-markdown": "catalog:",
|
||||
"typedoc-vitepress-theme": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitepress": "catalog:",
|
||||
"vitepress-knowledge": "catalog:",
|
||||
"vitepress-plugin-group-icons": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"@aklinker1/buildc": "^1.1.4",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"@commitlint/config-conventional": "^19.8.0",
|
||||
"@commitlint/types": "^19.8.0",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@vitest/coverage-v8": "^3.1.2",
|
||||
"changelogen": "^0.6.1",
|
||||
"consola": "^3.4.2",
|
||||
"fast-glob": "^3.3.3",
|
||||
"feed": "^4.2.2",
|
||||
"fs-extra": "^11.3.0",
|
||||
"lint-staged": "^15.5.1",
|
||||
"markdown-it-footnote": "^4.0.0",
|
||||
"nano-spawn": "^0.2.0",
|
||||
"prettier": "^3.5.3",
|
||||
"simple-git-hooks": "^2.13.0",
|
||||
"tsx": "4.19.4",
|
||||
"typedoc": "^0.25.4",
|
||||
"typedoc-plugin-frontmatter": "^1.3.0",
|
||||
"typedoc-plugin-markdown": "4.0.0-next.23",
|
||||
"typedoc-vitepress-theme": "1.0.0-next.3",
|
||||
"typescript": "^5.8.3",
|
||||
"vitepress": "^1.6.3",
|
||||
"vitepress-knowledge": "^0.4.1",
|
||||
"vitepress-plugin-group-icons": "^1.5.2",
|
||||
"vitest-mock-extended": "^3.1.0",
|
||||
"vue": "^3.5.13",
|
||||
"wxt": "workspace:*"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
|
||||
@@ -9,11 +9,12 @@ import type {
|
||||
AnalyticsEventMetadata,
|
||||
AnalyticsProvider,
|
||||
} from './types';
|
||||
import { browser } from '@wxt-dev/browser';
|
||||
|
||||
const ANALYTICS_PORT = '@wxt-dev/analytics';
|
||||
|
||||
export function createAnalytics(config?: AnalyticsConfig): Analytics {
|
||||
if (typeof chrome === 'undefined' || !chrome?.runtime?.id)
|
||||
if (!browser?.runtime?.id)
|
||||
throw Error(
|
||||
'Cannot use WXT analytics in contexts without access to the browser.runtime APIs',
|
||||
);
|
||||
@@ -51,13 +52,13 @@ function createBackgroundAnalytics(
|
||||
defineStorageItem<boolean>('local:wxt-analytics:enabled', false);
|
||||
|
||||
// Cached values
|
||||
const platformInfo = chrome.runtime.getPlatformInfo();
|
||||
const platformInfo = browser.runtime.getPlatformInfo();
|
||||
const userAgent = UAParser();
|
||||
let userId = Promise.resolve(userIdStorage.getValue()).then(
|
||||
(id) => id ?? globalThis.crypto.randomUUID(),
|
||||
);
|
||||
let userProperties = userPropertiesStorage.getValue();
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
const manifest = browser.runtime.getManifest();
|
||||
|
||||
const getBackgroundMeta = () => ({
|
||||
timestamp: Date.now(),
|
||||
@@ -178,7 +179,7 @@ function createBackgroundAnalytics(
|
||||
config?.providers?.map((provider) => provider(analytics, config)) ?? [];
|
||||
|
||||
// Listen for messages from the rest of the extension
|
||||
chrome.runtime.onConnect.addListener((port) => {
|
||||
browser.runtime.onConnect.addListener((port) => {
|
||||
if (port.name === ANALYTICS_PORT) {
|
||||
port.onMessage.addListener(({ fn, args }) => {
|
||||
// @ts-expect-error: Untyped fn key
|
||||
@@ -194,7 +195,7 @@ function createBackgroundAnalytics(
|
||||
* Creates an analytics client for non-background contexts.
|
||||
*/
|
||||
function createFrontendAnalytics(): Analytics {
|
||||
const port = chrome.runtime.connect({ name: ANALYTICS_PORT });
|
||||
const port = browser.runtime.connect({ name: ANALYTICS_PORT });
|
||||
const sessionId = Date.now();
|
||||
const getFrontendMetadata = (): AnalyticsEventMetadata => ({
|
||||
sessionId,
|
||||
@@ -252,8 +253,8 @@ function defineStorageItem<T>(
|
||||
): AnalyticsStorageItem<T> {
|
||||
return {
|
||||
getValue: async () =>
|
||||
(await chrome.storage.local.get(key))[key] ?? defaultValue,
|
||||
setValue: (newValue) => chrome.storage.local.set({ [key]: newValue }),
|
||||
(await browser.storage.local.get(key))[key] ?? defaultValue,
|
||||
setValue: (newValue) => browser.storage.local.set({ [key]: newValue }),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -51,15 +51,15 @@
|
||||
"wxt": ">=0.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@types/chrome": "catalog:",
|
||||
"@types/ua-parser-js": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"wxt": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"ua-parser-js": "catalog:"
|
||||
"@wxt-dev/browser": "workspace:^",
|
||||
"ua-parser-js": "^1.0.40"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"#analytics": ["./.wxt/analytics/index.ts"]
|
||||
},
|
||||
"types": ["chrome"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
@@ -45,16 +45,16 @@
|
||||
"wxt": ">=0.19.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"defu": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"sharp": "catalog:"
|
||||
"defu": "^6.1.4",
|
||||
"fs-extra": "^11.3.0",
|
||||
"sharp": "^0.34.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"oxlint": "^0.16.8",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@wxt-dev/browser",
|
||||
"description": "Provides a cross-browser API for using extension APIs and types based on @types/chrome",
|
||||
"version": "0.0.315",
|
||||
"version": "0.0.318",
|
||||
"type": "module",
|
||||
"main": "src/index.mjs",
|
||||
"types": "src/index.d.ts",
|
||||
@@ -23,12 +23,12 @@
|
||||
"src"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/chrome": "0.0.315",
|
||||
"fs-extra": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
"@types/chrome": "0.0.318",
|
||||
"fs-extra": "^11.3.0",
|
||||
"nano-spawn": "^0.2.0",
|
||||
"tsx": "4.19.4",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^3.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/filesystem": "*",
|
||||
|
||||
@@ -76,5 +76,7 @@ function transformFile(file: string, content: string): string {
|
||||
.replaceAll('declare namespace chrome', 'export namespace Browser')
|
||||
// Update references to `chrome` namespace to `Browser`
|
||||
.replaceAll('chrome.', 'Browser.')
|
||||
// Fix links to developer.chrome.com
|
||||
.replaceAll('developer.Browser.com', 'developer.chrome.com')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7480,7 +7480,7 @@ export namespace Browser {
|
||||
*/
|
||||
export namespace permissions {
|
||||
export interface Permissions {
|
||||
/** The list of host permissions, including those specified in the `optional_permissions` or `permissions` keys in the manifest, and those associated with [Content Scripts](https://developer.Browser.com/docs/extensions/develop/concepts/content-scripts). */
|
||||
/** The list of host permissions, including those specified in the `optional_permissions` or `permissions` keys in the manifest, and those associated with [Content Scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts). */
|
||||
origins?: string[];
|
||||
/** List of named permissions (does not include hosts or origins). */
|
||||
permissions?: Browser.runtime.ManifestPermissions[];
|
||||
@@ -8498,7 +8498,7 @@ export namespace Browser {
|
||||
}
|
||||
|
||||
export interface ConnectionOptions {
|
||||
/** Optional. Flag indicating whether or not the connection should be left open when the application is suspended (see Manage App Lifecycle: https://developer.Browser.com/apps/app_lifecycle).
|
||||
/** Optional. Flag indicating whether or not the connection should be left open when the application is suspended (see Manage App Lifecycle: https://developer.chrome.com/apps/app_lifecycle).
|
||||
* The default value is "false." When the application is loaded, any serial connections previously opened with persistent=true can be fetched with getConnections. */
|
||||
persistent?: boolean | undefined;
|
||||
/** Optional. An application-defined string to associate with the connection. */
|
||||
@@ -8716,13 +8716,13 @@ export namespace Browser {
|
||||
/** The ID of the extension/app. */
|
||||
export var id: string;
|
||||
|
||||
/** https://developer.Browser.com/docs/extensions/reference/api/runtime#type-PlatformOs */
|
||||
/** https://developer.chrome.com/docs/extensions/reference/api/runtime#type-PlatformOs */
|
||||
export type PlatformOs = "mac" | "win" | "android" | "cros" | "linux" | "openbsd" | "fuchsia";
|
||||
/** https://developer.Browser.com/docs/extensions/reference/api/runtime#type-PlatformArch */
|
||||
/** https://developer.chrome.com/docs/extensions/reference/api/runtime#type-PlatformArch */
|
||||
export type PlatformArch = "arm" | "arm64" | "x86-32" | "x86-64" | "mips" | "mips64";
|
||||
/** https://developer.Browser.com/docs/extensions/reference/api/runtime#type-PlatformNaclArch */
|
||||
/** https://developer.chrome.com/docs/extensions/reference/api/runtime#type-PlatformNaclArch */
|
||||
export type PlatformNaclArch = "arm" | "x86-32" | "x86-64" | "mips" | "mips64";
|
||||
/** https://developer.Browser.com/docs/extensions/reference/api/runtime#type-ContextType */
|
||||
/** https://developer.chrome.com/docs/extensions/reference/api/runtime#type-ContextType */
|
||||
export enum ContextType {
|
||||
TAB = "TAB",
|
||||
POPUP = "POPUP",
|
||||
@@ -8731,7 +8731,7 @@ export namespace Browser {
|
||||
SIDE_PANEL = "SIDE_PANEL",
|
||||
DEVELOPER_TOOLS = "DEVELOPER_TOOLS",
|
||||
}
|
||||
/** https://developer.Browser.com/docs/extensions/reference/api/runtime#type-OnInstalledReason */
|
||||
/** https://developer.chrome.com/docs/extensions/reference/api/runtime#type-OnInstalledReason */
|
||||
export enum OnInstalledReason {
|
||||
INSTALL = "install",
|
||||
UPDATE = "update",
|
||||
@@ -8958,7 +8958,7 @@ export namespace Browser {
|
||||
default_popup?: string | undefined;
|
||||
}
|
||||
|
||||
// Source: https://developer.Browser.com/docs/extensions/mv3/declare_permissions/
|
||||
/** Source: https://developer.chrome.com/docs/extensions/reference/permissions-list */
|
||||
export type ManifestPermissions =
|
||||
| "accessibilityFeatures.modify"
|
||||
| "accessibilityFeatures.read"
|
||||
@@ -9015,11 +9015,11 @@ export namespace Browser {
|
||||
| "privacy"
|
||||
| "processes"
|
||||
| "proxy"
|
||||
| "readingList"
|
||||
| "scripting"
|
||||
| "search"
|
||||
| "sessions"
|
||||
| "sidePanel"
|
||||
| "signedInDevices"
|
||||
| "storage"
|
||||
| "system.cpu"
|
||||
| "system.display"
|
||||
@@ -9042,6 +9042,23 @@ export namespace Browser {
|
||||
| "webRequestBlocking"
|
||||
| "webRequestAuthProvider";
|
||||
|
||||
/** Source : https://developer.chrome.com/docs/extensions/reference/api/permissions */
|
||||
export type ManifestOptionalPermissions = Exclude<
|
||||
ManifestPermissions,
|
||||
| "debugger"
|
||||
| "declarativeNetRequest"
|
||||
| "devtools"
|
||||
| "experimental"
|
||||
| "fontSettings"
|
||||
| "geolocation"
|
||||
| "proxy"
|
||||
| "tts"
|
||||
| "ttsEngine"
|
||||
| "unlimitedStorage"
|
||||
| "wallpaper"
|
||||
| "webAuthenticationProxy"
|
||||
>;
|
||||
|
||||
export interface SearchProvider {
|
||||
name?: string | undefined;
|
||||
keyword?: string | undefined;
|
||||
@@ -9261,8 +9278,8 @@ export namespace Browser {
|
||||
}
|
||||
| undefined;
|
||||
content_security_policy?: string | undefined;
|
||||
optional_permissions?: string[] | undefined;
|
||||
permissions?: string[] | undefined;
|
||||
optional_permissions?: ManifestOptionalPermissions[] | string[] | undefined;
|
||||
permissions?: ManifestPermissions[] | string[] | undefined;
|
||||
web_accessible_resources?: string[] | undefined;
|
||||
}
|
||||
|
||||
@@ -9297,7 +9314,7 @@ export namespace Browser {
|
||||
sandbox?: string;
|
||||
};
|
||||
host_permissions?: string[] | undefined;
|
||||
optional_permissions?: ManifestPermissions[] | undefined;
|
||||
optional_permissions?: ManifestOptionalPermissions[] | undefined;
|
||||
optional_host_permissions?: string[] | undefined;
|
||||
permissions?: ManifestPermissions[] | undefined;
|
||||
web_accessible_resources?: Array<{ resources: string[]; matches: string[] }> | undefined;
|
||||
@@ -12211,131 +12228,111 @@ export namespace Browser {
|
||||
*/
|
||||
export namespace tabGroups {
|
||||
/** An ID that represents the absence of a group. */
|
||||
export var TAB_GROUP_ID_NONE: -1;
|
||||
export const TAB_GROUP_ID_NONE: -1;
|
||||
|
||||
export type ColorEnum = "grey" | "blue" | "red" | "yellow" | "green" | "pink" | "purple" | "cyan" | "orange";
|
||||
/** The group's color. */
|
||||
export enum Color {
|
||||
BLUE = "blue",
|
||||
CYAN = "cyan",
|
||||
GREEN = "green",
|
||||
GREY = "grey",
|
||||
ORANGE = "orange",
|
||||
PINK = "pink",
|
||||
PURPLE = "purple",
|
||||
RED = "red",
|
||||
YELLOW = "yellow",
|
||||
}
|
||||
|
||||
export interface TabGroup {
|
||||
/** Whether the group is collapsed. A collapsed group is one whose tabs are hidden. */
|
||||
collapsed: boolean;
|
||||
/** The group's color. */
|
||||
color: ColorEnum;
|
||||
color: `${Color}`;
|
||||
/** The ID of the group. Group IDs are unique within a browser session. */
|
||||
id: number;
|
||||
/** Optional. The title of the group. */
|
||||
title?: string | undefined;
|
||||
/** The title of the group. */
|
||||
title?: string;
|
||||
/** The ID of the window that contains the group. */
|
||||
windowId: number;
|
||||
}
|
||||
|
||||
export interface MoveProperties {
|
||||
/** The position to move the group to. Use -1 to place the group at the end of the window. */
|
||||
/** The position to move the group to. Use `-1` to place the group at the end of the window. */
|
||||
index: number;
|
||||
/** Optional. The window to move the group to. Defaults to the window the group is currently in. Note that groups can only be moved to and from windows with Browser.windows.WindowType type "normal". */
|
||||
windowId?: number | undefined;
|
||||
/** The window to move the group to. Defaults to the window the group is currently in. Note that groups can only be moved to and from windows with {@link windows.windowTypeEnum windows.windowType} type `"normal"`. */
|
||||
windowId?: number;
|
||||
}
|
||||
|
||||
export interface QueryInfo {
|
||||
/** Optional. Whether the groups are collapsed. */
|
||||
collapsed?: boolean | undefined;
|
||||
/** Optional. The color of the groups. */
|
||||
color?: ColorEnum | undefined;
|
||||
/** Optional. Match group titles against a pattern. */
|
||||
title?: string | undefined;
|
||||
/** Optional. The ID of the window that contains the group. */
|
||||
windowId?: number | undefined;
|
||||
/** Whether the groups are collapsed. */
|
||||
collapsed?: boolean;
|
||||
/** The color of the groups. */
|
||||
color?: `${Color}`;
|
||||
/** Match group titles against a pattern. */
|
||||
title?: string;
|
||||
/** The ID of the parent window, or {@link windows.WINDOW_ID_CURRENT} for the current window. */
|
||||
windowId?: number;
|
||||
}
|
||||
|
||||
export interface UpdateProperties {
|
||||
/** Optional. Whether the group should be collapsed. */
|
||||
collapsed?: boolean | undefined;
|
||||
/** Optional. The color of the group. */
|
||||
color?: ColorEnum | undefined;
|
||||
/** Optional. The title of the group. */
|
||||
title?: string | undefined;
|
||||
/** Whether the group should be collapsed. */
|
||||
collapsed?: boolean;
|
||||
/** The color of the group. */
|
||||
color?: `${Color}`;
|
||||
/** The title of the group. */
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves details about the specified group.
|
||||
* @param groupId The ID of the tab group.
|
||||
* @param callback Called with the retrieved tab group.
|
||||
*
|
||||
* Can return its result via Promise since Chrome 90.
|
||||
*/
|
||||
export function get(groupId: number): Promise<TabGroup>;
|
||||
export function get(groupId: number, callback: (group: TabGroup) => void): void;
|
||||
|
||||
/**
|
||||
* Retrieves details about the specified group.
|
||||
* @param groupId The ID of the tab group.
|
||||
* @return The `get` method provides its result via callback or returned as a `Promise` (MV3 only).
|
||||
*/
|
||||
export function get(groupId: number): Promise<TabGroup>;
|
||||
|
||||
/**
|
||||
* Moves the group and all its tabs within its window, or to a new window.
|
||||
* @param groupId The ID of the group to move.
|
||||
* @param moveProperties Information on how to move the group.
|
||||
* @return The `move` method provides its result via callback or returned as a `Promise` (MV3 only).
|
||||
*/
|
||||
export function move(groupId: number, moveProperties: MoveProperties): Promise<TabGroup>;
|
||||
|
||||
/**
|
||||
* Moves the group and all its tabs within its window, or to a new window.
|
||||
* @param groupId The ID of the group to move.
|
||||
* @param moveProperties Information on how to move the group.
|
||||
* @param callback Optional.
|
||||
*
|
||||
* Can return its result via Promise since Chrome 90.
|
||||
*/
|
||||
export function move(groupId: number, moveProperties: MoveProperties): Promise<TabGroup | undefined>;
|
||||
export function move(
|
||||
groupId: number,
|
||||
moveProperties: MoveProperties,
|
||||
callback: (group: TabGroup) => void,
|
||||
callback: (group?: TabGroup) => void,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Gets all groups that have the specified properties, or all groups if no properties are specified.
|
||||
* @param queryInfo Object with search parameters.
|
||||
* @param callback Called with retrieved tab groups.
|
||||
*
|
||||
* Can return its result via Promise since Chrome 90.
|
||||
*/
|
||||
export function query(queryInfo: QueryInfo): Promise<TabGroup[]>;
|
||||
export function query(queryInfo: QueryInfo, callback: (result: TabGroup[]) => void): void;
|
||||
|
||||
/**
|
||||
* Gets all groups that have the specified properties, or all groups if no properties are specified.
|
||||
* @param queryInfo Object with search parameters.
|
||||
* @return The `query` method provides its result via callback or returned as a `Promise` (MV3 only).
|
||||
*/
|
||||
export function query(queryInfo: QueryInfo): Promise<TabGroup[]>;
|
||||
|
||||
/**
|
||||
* Modifies the properties of a group. Properties that are not specified in updateProperties are not modified.
|
||||
* Modifies the properties of a group. Properties that are not specified in `updateProperties` are not modified.
|
||||
* @param groupId The ID of the group to modify.
|
||||
* @param updateProperties Information on how to update the group.
|
||||
* @return The `update` method provides its result via callback or returned as a `Promise` (MV3 only).
|
||||
*/
|
||||
export function update(groupId: number, updateProperties: UpdateProperties): Promise<TabGroup>;
|
||||
|
||||
/**
|
||||
* Modifies the properties of a group. Properties that are not specified in updateProperties are not modified.
|
||||
* @param groupId The ID of the group to modify.
|
||||
* @param updateProperties Information on how to update the group.
|
||||
* @param callback Optional.
|
||||
*
|
||||
* Can return its result via Promise since Chrome 90.
|
||||
*/
|
||||
export function update(groupId: number, updateProperties: UpdateProperties): Promise<TabGroup | undefined>;
|
||||
export function update(
|
||||
groupId: number,
|
||||
updateProperties: UpdateProperties,
|
||||
callback: (group: TabGroup) => void,
|
||||
callback: (group?: TabGroup) => void,
|
||||
): void;
|
||||
|
||||
export interface TabGroupCreatedEvent extends Browser.events.Event<(group: TabGroup) => void> {}
|
||||
export interface TabGroupMovedEvent extends Browser.events.Event<(group: TabGroup) => void> {}
|
||||
export interface TabGroupRemovedEvent extends Browser.events.Event<(group: TabGroup) => void> {}
|
||||
export interface TabGroupUpdated extends Browser.events.Event<(group: TabGroup) => void> {}
|
||||
|
||||
/** Fired when a group is created. */
|
||||
export var onCreated: TabGroupCreatedEvent;
|
||||
export const onCreated: events.Event<(group: TabGroup) => void>;
|
||||
/** Fired when a group is moved within a window. Move events are still fired for the individual tabs within the group, as well as for the group itself. This event is not fired when a group is moved between windows; instead, it will be removed from one window and created in another. */
|
||||
export var onMoved: TabGroupMovedEvent;
|
||||
/** Fired when a group is closed, either directly by the user or automatically because it contained zero. */
|
||||
export var onRemoved: TabGroupRemovedEvent;
|
||||
export const onMoved: events.Event<(group: TabGroup) => void>;
|
||||
/** Fired when a group is closed, either directly by the user or automatically because it contained zero tabs. */
|
||||
export const onRemoved: events.Event<(group: TabGroup) => void>;
|
||||
/** Fired when a group is updated. */
|
||||
export var onUpdated: TabGroupUpdated;
|
||||
export const onUpdated: events.Event<(group: TabGroup) => void>;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
@@ -14845,6 +14842,18 @@ export namespace Browser {
|
||||
*/
|
||||
export type ExecutionWorld = "MAIN" | "USER_SCRIPT";
|
||||
|
||||
/** @since Chrome 135 */
|
||||
export interface InjectionResult {
|
||||
/** The document associated with the injection. */
|
||||
documentId: string;
|
||||
/** The error, if any. `error` and `result` are mutually exclusive. */
|
||||
error?: string;
|
||||
/** The frame associated with the injection. */
|
||||
frameId: number;
|
||||
/** The result of the script execution. */
|
||||
result: any;
|
||||
}
|
||||
|
||||
export interface WorldProperties {
|
||||
/** Specifies the world csp. The default is the `ISOLATED` world csp. */
|
||||
csp?: string;
|
||||
@@ -14861,6 +14870,18 @@ export namespace Browser {
|
||||
ids?: string[];
|
||||
}
|
||||
|
||||
/** @since Chrome 135 */
|
||||
export interface InjectionTarget {
|
||||
/** Whether the script should inject into all frames within the tab. Defaults to false. This must not be true if `frameIds` is specified. */
|
||||
allFrames?: boolean;
|
||||
/** The IDs of specific documentIds to inject into. This must not be set if `frameIds` is set. */
|
||||
documentIds?: string[];
|
||||
/** The IDs of specific frames to inject into. */
|
||||
frameIds?: number[];
|
||||
/** The ID of the tab into which to inject. */
|
||||
tabId: number;
|
||||
}
|
||||
|
||||
export interface RegisteredUserScript {
|
||||
/** If true, it will inject into all frames, even if the frame is not the top-most frame in the tab. Each frame is checked independently for URL requirements; it will not inject into child frames if the URL requirements are not met. Defaults to false, meaning that only the top frame is matched. */
|
||||
allFrames?: boolean;
|
||||
@@ -14887,6 +14908,20 @@ export namespace Browser {
|
||||
worldId?: string;
|
||||
}
|
||||
|
||||
/** @since Chrome 135 */
|
||||
export interface UserScriptInjection {
|
||||
/** Whether the injection should be triggered in the target as soon as possible. Note that this is not a guarantee that injection will occur prior to page load, as the page may have already loaded by the time the script reaches the target. */
|
||||
injectImmediately?: boolean;
|
||||
/** The list of ScriptSource objects defining sources of scripts to be injected into the target. */
|
||||
js: ScriptSource[];
|
||||
/** Details specifying the target into which to inject the script. */
|
||||
target: InjectionTarget;
|
||||
/** The JavaScript "world" to run the script in. The default is `USER_SCRIPT`. */
|
||||
world?: ExecutionWorld;
|
||||
/** Specifies the user script world ID to execute in. If omitted, the script will execute in the default user script world. Only valid if `world` is omitted or is `USER_SCRIPT`. Values with leading underscores (`_`) are reserved. */
|
||||
worldId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for a script source.
|
||||
*/
|
||||
@@ -14939,6 +14974,13 @@ export namespace Browser {
|
||||
export function getWorldConfigurations(): Promise<WorldProperties[]>;
|
||||
export function getWorldConfigurations(callback: (worlds: WorldProperties[]) => void): void;
|
||||
|
||||
/**
|
||||
* Injects a script into a target context. By default, the script will be run at `document_idle`, or immediately if the page has already loaded. If the `injectImmediately` property is set, the script will inject without waiting, even if the page has not finished loading. If the script evaluates to a promise, the browser will wait for the promise to settle and return the resulting value.
|
||||
* @since Chrome 135
|
||||
*/
|
||||
export function execute(injection: UserScriptInjection): Promise<InjectionResult[]>;
|
||||
export function execute(injection: UserScriptInjection, callback: (result: InjectionResult[]) => void): void;
|
||||
|
||||
/**
|
||||
* Registers one or more user scripts for this extension.
|
||||
*
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/chrome": "{{chromeTypesVersion}}",
|
||||
"fs-extra": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
"fs-extra": "^11.3.0",
|
||||
"nano-spawn": "^0.2.0",
|
||||
"tsx": "4.19.4",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^3.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## v0.2.4
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/i18n-v0.2.3...i18n-v0.2.4)
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Standardize locale codes and warn about unsupported ones ([#1617](https://github.com/wxt-dev/wxt/pull/1617))
|
||||
- Use `@wxt-dev/browser` instead of `@types/chrome` ([#1645](https://github.com/wxt-dev/wxt/pull/1645))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Add react language ID to README ([#1347](https://github.com/wxt-dev/wxt/pull/1347))
|
||||
- Fix public path reference ([bcb20874](https://github.com/wxt-dev/wxt/commit/bcb20874))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **deps:** Upgrade all non-major dependencies ([#1164](https://github.com/wxt-dev/wxt/pull/1164))
|
||||
- **deps:** Bump dev and non-breaking major dependencies ([#1167](https://github.com/wxt-dev/wxt/pull/1167))
|
||||
- Add funding links to `package.json` files ([#1446](https://github.com/wxt-dev/wxt/pull/1446))
|
||||
- Use PNPM 10's new catelog feature ([#1493](https://github.com/wxt-dev/wxt/pull/1493))
|
||||
- Move production dependencies to PNPM 10 catelog ([#1494](https://github.com/wxt-dev/wxt/pull/1494))
|
||||
- Stop using PNPM catalog ([#1644](https://github.com/wxt-dev/wxt/pull/1644))
|
||||
- Upgrade `@aklinker1/check` to v2 ([#1647](https://github.com/wxt-dev/wxt/pull/1647))
|
||||
- Change browser workspace dependency to `^` ([c7335add](https://github.com/wxt-dev/wxt/commit/c7335add))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
- Okinea Dev ([@okineadev](https://github.com/okineadev))
|
||||
- Redwoodlid ([@redwoodlid](https://github.com/redwoodlid))
|
||||
|
||||
## v0.2.3
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/i18n-v0.2.2...i18n-v0.2.3)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@wxt-dev/i18n",
|
||||
"description": "Type-safe wrapper around browser.i18n.getMessage with additional features",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -26,9 +26,10 @@
|
||||
"test": "buildc --deps-only -- vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"chokidar": "catalog:",
|
||||
"confbox": "catalog:",
|
||||
"fast-glob": "catalog:"
|
||||
"@wxt-dev/browser": "workspace:^",
|
||||
"chokidar": "^4.0.3",
|
||||
"confbox": "^0.1.8 || ^0.2.2",
|
||||
"fast-glob": "^3.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"wxt": ">=0.19.7"
|
||||
@@ -39,15 +40,14 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@types/chrome": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-plugin-random-seed": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"@types/node": "^20.17.6",
|
||||
"oxlint": "^0.16.8",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"vitest": "^3.1.2",
|
||||
"vitest-plugin-random-seed": "^1.1.1",
|
||||
"wxt": "workspace:*"
|
||||
},
|
||||
"main": "./dist/index.cjs",
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { createI18n } from '../index';
|
||||
import { browser } from '@wxt-dev/browser';
|
||||
|
||||
const getMessageMock = vi.fn();
|
||||
|
||||
vi.stubGlobal('chrome', {
|
||||
i18n: {
|
||||
getMessage: getMessageMock,
|
||||
},
|
||||
vi.mock('@wxt-dev/browser', async () => {
|
||||
const { vi } = await import('vitest');
|
||||
return {
|
||||
browser: {
|
||||
i18n: {
|
||||
getMessage: vi.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
const getMessageMock = vi.mocked(browser.i18n.getMessage);
|
||||
|
||||
describe('createI18n', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { beforeEach, describe, it, vi } from 'vitest';
|
||||
import { createI18n } from '..';
|
||||
import { browser } from '@wxt-dev/browser';
|
||||
|
||||
const getMessageMock = vi.fn();
|
||||
|
||||
vi.stubGlobal('chrome', {
|
||||
i18n: {
|
||||
getMessage: getMessageMock,
|
||||
},
|
||||
vi.mock('@wxt-dev/browser', async () => {
|
||||
const { vi } = await import('vitest');
|
||||
return {
|
||||
browser: {
|
||||
i18n: {
|
||||
getMessage: vi.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
const getMessageMock = vi.mocked(browser.i18n.getMessage);
|
||||
|
||||
const n: number = 1;
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ChromeMessage } from '../build';
|
||||
import { applyChromeMessagePlaceholders, getSubstitutionCount } from '../utils';
|
||||
import {
|
||||
applyChromeMessagePlaceholders,
|
||||
getSubstitutionCount,
|
||||
standardizeLocale,
|
||||
} from '../utils';
|
||||
|
||||
describe('Utils', () => {
|
||||
describe('applyChromeMessagePlaceholders', () => {
|
||||
it('should return the combined stirng', () => {
|
||||
it('should return the combined string', () => {
|
||||
const input = {
|
||||
message: 'Hello $username$, welcome to $appName$',
|
||||
placeholders: {
|
||||
@@ -60,4 +64,30 @@ describe('Utils', () => {
|
||||
expect(getSubstitutionCount('Hello $10')).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('standardizeLocale', () => {
|
||||
it('should convert two-letter locale codes to lowercase', () => {
|
||||
expect(standardizeLocale('en')).toEqual('en');
|
||||
expect(standardizeLocale('EN')).toEqual('en');
|
||||
});
|
||||
|
||||
it('should convert locale code extensions to uppercase', () => {
|
||||
expect(standardizeLocale('en_US')).toEqual('en_US');
|
||||
expect(standardizeLocale('en_us')).toEqual('en_US');
|
||||
expect(standardizeLocale('es_419')).toEqual('es_419');
|
||||
});
|
||||
|
||||
it('should convert dashes to underscores', () => {
|
||||
expect(standardizeLocale('en_US')).toEqual('en_US');
|
||||
expect(standardizeLocale('en-US')).toEqual('en_US');
|
||||
});
|
||||
|
||||
it('should return the input string as-is for unknown formats', () => {
|
||||
expect(standardizeLocale('en_USSS')).toEqual('en_USSS');
|
||||
expect(standardizeLocale('en-')).toEqual('en-');
|
||||
expect(standardizeLocale('------')).toEqual('------');
|
||||
expect(standardizeLocale('test')).toEqual('test');
|
||||
expect(standardizeLocale('hello-world')).toEqual('hello-world');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ import { parseYAML, parseJSON5, parseTOML } from 'confbox';
|
||||
import { dirname, extname } from 'node:path';
|
||||
import { applyChromeMessagePlaceholders, getSubstitutionCount } from './utils';
|
||||
|
||||
export { SUPPORTED_LOCALES } from './supported-locales';
|
||||
|
||||
//
|
||||
// TYPES
|
||||
//
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
I18n,
|
||||
Substitution,
|
||||
} from './types';
|
||||
import { browser } from '@wxt-dev/browser';
|
||||
|
||||
export function createI18n<
|
||||
T extends I18nStructure = DefaultI18nStructure,
|
||||
@@ -39,9 +40,9 @@ export function createI18n<
|
||||
if (sub?.length) {
|
||||
// Convert all substitutions to strings
|
||||
const stringSubs = sub?.map((sub) => String(sub));
|
||||
message = chrome.i18n.getMessage(key.replaceAll('.', '_'), stringSubs);
|
||||
message = browser.i18n.getMessage(key.replaceAll('.', '_'), stringSubs);
|
||||
} else {
|
||||
message = chrome.i18n.getMessage(key.replaceAll('.', '_'));
|
||||
message = browser.i18n.getMessage(key.replaceAll('.', '_'));
|
||||
}
|
||||
if (!message) {
|
||||
console.warn(`[i18n] Message not found: "${key}"`);
|
||||
|
||||
@@ -16,12 +16,14 @@ import {
|
||||
generateChromeMessagesText,
|
||||
parseMessagesFile,
|
||||
generateTypeText,
|
||||
SUPPORTED_LOCALES,
|
||||
} from './build';
|
||||
import glob from 'fast-glob';
|
||||
import { basename, extname, join, resolve } from 'node:path';
|
||||
import { watch } from 'chokidar';
|
||||
import { GeneratedPublicFile, WxtDirFileEntry } from 'wxt';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { standardizeLocale } from './utils';
|
||||
|
||||
export default defineWxtModule<I18nOptions>({
|
||||
name: '@wxt-dev/i18n',
|
||||
@@ -46,10 +48,22 @@ export default defineWxtModule<I18nOptions>({
|
||||
cwd: localesDir,
|
||||
absolute: true,
|
||||
});
|
||||
return files.map((file) => ({
|
||||
file,
|
||||
locale: basename(file).replace(extname(file), ''),
|
||||
}));
|
||||
|
||||
const unsupportedLocales: string[] = [];
|
||||
|
||||
const res = files.map((file) => {
|
||||
const rawLocale = basename(file).replace(extname(file), '');
|
||||
const locale = standardizeLocale(rawLocale);
|
||||
if (!SUPPORTED_LOCALES.has(locale)) unsupportedLocales.push(locale);
|
||||
return { file, locale };
|
||||
});
|
||||
|
||||
if (unsupportedLocales.length > 0)
|
||||
wxt.logger.warn(
|
||||
`Unsupported locales: [${unsupportedLocales.join(', ')}].\n\nWeb extensions only support a limited set of locales as described here: https://developer.chrome.com/docs/extensions/reference/api/i18n#locales`,
|
||||
);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
const generateOutputJsonFiles = async (): Promise<
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/** From https://developer.chrome.com/docs/extensions/reference/api/i18n#locales */
|
||||
export const SUPPORTED_LOCALES = new Set([
|
||||
'ar',
|
||||
'am',
|
||||
'bg',
|
||||
'bn',
|
||||
'ca',
|
||||
'cs',
|
||||
'da',
|
||||
'de',
|
||||
'el',
|
||||
'en',
|
||||
'en_AU',
|
||||
'en_GB',
|
||||
'en_US',
|
||||
'es',
|
||||
'es_419',
|
||||
'et',
|
||||
'fa',
|
||||
'fi',
|
||||
'fil',
|
||||
'fr',
|
||||
'gu',
|
||||
'he',
|
||||
'hi',
|
||||
'hr',
|
||||
'hu',
|
||||
'id',
|
||||
'it',
|
||||
'ja',
|
||||
'kn',
|
||||
'ko',
|
||||
'lt',
|
||||
'lv',
|
||||
'ml',
|
||||
'mr',
|
||||
'ms',
|
||||
'nl',
|
||||
'no',
|
||||
'pl',
|
||||
'pt_BR',
|
||||
'pt_PT',
|
||||
'ro',
|
||||
'ru',
|
||||
'sk',
|
||||
'sl',
|
||||
'sr',
|
||||
'sv',
|
||||
'sw',
|
||||
'ta',
|
||||
'te',
|
||||
'th',
|
||||
'tr',
|
||||
'uk',
|
||||
'vi',
|
||||
'zh_CN',
|
||||
'zh_TW',
|
||||
]);
|
||||
@@ -21,3 +21,16 @@ export function getSubstitutionCount(message: string): number {
|
||||
}
|
||||
|
||||
const MAX_SUBSTITUTIONS = 9;
|
||||
|
||||
/** Given a string, standardize it to the format `xx_YY`. */
|
||||
export function standardizeLocale(locale: string): string {
|
||||
if (locale.length === 2) return locale.toLowerCase();
|
||||
|
||||
const [is_match, prefix, suffix] =
|
||||
locale.match(/^([a-z]{2})[-_]([a-z]{2,3})$/i) ?? [];
|
||||
if (is_match) {
|
||||
return `${prefix.toLowerCase()}_${suffix.toUpperCase()}`;
|
||||
}
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["chrome", "node"]
|
||||
"types": ["node"]
|
||||
},
|
||||
"exclude": ["node_modules/**", "dist/**"]
|
||||
}
|
||||
|
||||
@@ -48,17 +48,17 @@
|
||||
"wxt": ">=0.19.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-react": "catalog:"
|
||||
"@vitejs/plugin-react": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.3",
|
||||
"publint": "^0.3.12",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,14 +48,14 @@
|
||||
"wxt": ">=0.19.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"vite-plugin-solid": "catalog:"
|
||||
"vite-plugin-solid": "^2.11.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"solid-js": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"publint": "^0.3.12",
|
||||
"solid-js": "^1.9.6",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@
|
||||
"svelte": ">=5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "catalog:"
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@
|
||||
"wxt": ">=0.19.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-vue": "catalog:"
|
||||
"@vitejs/plugin-vue": "^5.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,18 +30,18 @@
|
||||
"test": "buildc --deps-only -- vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"async-mutex": "catalog:",
|
||||
"dequal": "catalog:"
|
||||
"@wxt-dev/browser": "workspace:^",
|
||||
"async-mutex": "^0.5.0",
|
||||
"dequal": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@types/chrome": "catalog:",
|
||||
"@webext-core/fake-browser": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"@webext-core/fake-browser": "^1.3.2",
|
||||
"oxlint": "^0.16.8",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"vitest": "^3.1.2"
|
||||
},
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { fakeBrowser } from '@webext-core/fake-browser';
|
||||
import { describe, it, expect, beforeEach, vi, expectTypeOf } from 'vitest';
|
||||
import { MigrationError, type WxtStorageItem, storage } from '../index';
|
||||
import { browser } from '@wxt-dev/browser';
|
||||
|
||||
/**
|
||||
* This works because fakeBrowser is synchronous, and is will finish any number of chained
|
||||
@@ -222,7 +223,7 @@ describe('Storage Utils', () => {
|
||||
describe('setMeta', () => {
|
||||
it('should set metadata at key+$', async () => {
|
||||
const existing = { v: 1 };
|
||||
await chrome.storage[storageArea].set({ count$: existing });
|
||||
await browser.storage[storageArea].set({ count$: existing });
|
||||
const newValues = {
|
||||
date: Date.now(),
|
||||
};
|
||||
@@ -238,7 +239,7 @@ describe('Storage Utils', () => {
|
||||
'should remove any properties set to %s',
|
||||
async (version) => {
|
||||
const existing = { v: 1 };
|
||||
await chrome.storage[storageArea].set({ count$: existing });
|
||||
await browser.storage[storageArea].set({ count$: existing });
|
||||
const expected = {};
|
||||
|
||||
await storage.setMeta(`${storageArea}:count`, { v: version });
|
||||
@@ -1265,7 +1266,7 @@ describe('Storage Utils', () => {
|
||||
|
||||
await item.removeValue();
|
||||
// Make sure it's actually blank before running the test
|
||||
expect(await chrome.storage.local.get()).toEqual({});
|
||||
expect(await browser.storage.local.get()).toEqual({});
|
||||
init.mockClear();
|
||||
|
||||
const [value1, value2] = await Promise.all([
|
||||
@@ -1281,6 +1282,19 @@ describe('Storage Utils', () => {
|
||||
it('should define a nullable value when options are not passed', () => {
|
||||
const item = storage.defineItem<number>(`local:test`);
|
||||
expectTypeOf(item).toEqualTypeOf<WxtStorageItem<number | null, {}>>();
|
||||
|
||||
const item2 = storage.defineItem<number>(`local:test`, {});
|
||||
expectTypeOf(item2).toEqualTypeOf<WxtStorageItem<number | null, {}>>();
|
||||
|
||||
const item3 = storage.defineItem<number>(`local:test`, {
|
||||
fallback: undefined,
|
||||
});
|
||||
expectTypeOf(item3).toEqualTypeOf<WxtStorageItem<number | null, {}>>();
|
||||
|
||||
const item4 = storage.defineItem<number>(`local:test`, {
|
||||
defaultValue: undefined,
|
||||
});
|
||||
expectTypeOf(item4).toEqualTypeOf<WxtStorageItem<number | null, {}>>();
|
||||
});
|
||||
|
||||
it('should define a non-null value when options are passed with a nullish default value', () => {
|
||||
@@ -1288,6 +1302,11 @@ describe('Storage Utils', () => {
|
||||
defaultValue: 123,
|
||||
});
|
||||
expectTypeOf(item).toEqualTypeOf<WxtStorageItem<number, {}>>();
|
||||
|
||||
const item2 = storage.defineItem(`local:test`, {
|
||||
fallback: 123,
|
||||
});
|
||||
expectTypeOf(item2).toEqualTypeOf<WxtStorageItem<number, {}>>();
|
||||
});
|
||||
|
||||
it('should define a nullable value when options are passed with null default value', () => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/// <reference types="chrome" />
|
||||
/**
|
||||
* Simplified storage APIs with support for versioned fields, snapshots, metadata, and item definitions.
|
||||
*
|
||||
@@ -7,13 +6,7 @@
|
||||
*/
|
||||
import { dequal } from 'dequal/lite';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
const browser: typeof chrome =
|
||||
// @ts-expect-error
|
||||
globalThis.browser?.runtime?.id == null
|
||||
? globalThis.chrome
|
||||
: // @ts-expect-error
|
||||
globalThis.browser;
|
||||
import { browser, type Browser } from '@wxt-dev/browser';
|
||||
|
||||
export const storage = createStorage();
|
||||
|
||||
@@ -750,8 +743,16 @@ export interface WxtStorage {
|
||||
): WxtStorageItem<TValue | null, TMetadata>;
|
||||
defineItem<TValue, TMetadata extends Record<string, unknown> = {}>(
|
||||
key: StorageItemKey,
|
||||
options: WxtStorageItemOptions<TValue>,
|
||||
options: WxtStorageItemOptions<TValue> & { fallback: TValue },
|
||||
): WxtStorageItem<TValue, TMetadata>;
|
||||
defineItem<TValue, TMetadata extends Record<string, unknown> = {}>(
|
||||
key: StorageItemKey,
|
||||
options: WxtStorageItemOptions<TValue> & { defaultValue: TValue },
|
||||
): WxtStorageItem<TValue, TMetadata>;
|
||||
defineItem<TValue, TMetadata extends Record<string, unknown> = {}>(
|
||||
key: StorageItemKey,
|
||||
options: WxtStorageItemOptions<TValue>,
|
||||
): WxtStorageItem<TValue | null, TMetadata>;
|
||||
}
|
||||
|
||||
interface WxtStorageDriver {
|
||||
@@ -889,7 +890,7 @@ export interface WxtStorageItemOptions<T> {
|
||||
}
|
||||
|
||||
export type StorageAreaChanges = {
|
||||
[key: string]: chrome.storage.StorageChange;
|
||||
[key: string]: Browser.storage.StorageChange;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"verbatimModuleSyntax": true,
|
||||
"types": ["chrome"]
|
||||
"verbatimModuleSyntax": true
|
||||
},
|
||||
"exclude": ["node_modules/**", "dist/**"]
|
||||
}
|
||||
|
||||
@@ -40,16 +40,16 @@
|
||||
"wxt": ">=0.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"unocss": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"oxlint": "^0.16.8",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"unocss": "^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0",
|
||||
"wxt": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"defu": "catalog:",
|
||||
"fast-glob": "catalog:"
|
||||
"defu": "^6.1.4",
|
||||
"fast-glob": "^3.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@
|
||||
"wxt": ">=0.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@types/webextension-polyfill": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"webextension-polyfill": "catalog:",
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"@types/webextension-polyfill": "^0.12.3",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"webextension-polyfill": "^0.12.0",
|
||||
"wxt": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@wxt-dev/i18n": "workspace:*",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.3",
|
||||
"@wxt-dev/auto-icons": "workspace:*",
|
||||
"@wxt-dev/unocss": "workspace:*",
|
||||
"sass": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unocss": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-plugin-random-seed": "catalog:",
|
||||
"sass": "^1.87.0",
|
||||
"typescript": "^5.8.3",
|
||||
"unocss": "^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0",
|
||||
"vitest": "^3.1.2",
|
||||
"vitest-plugin-random-seed": "^1.1.1",
|
||||
"wxt": "workspace:*"
|
||||
},
|
||||
"buildc": {
|
||||
|
||||
@@ -1,5 +1,63 @@
|
||||
# Changelog
|
||||
|
||||
## v0.20.6
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.5...wxt-v0.20.6)
|
||||
|
||||
## v0.20.5
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.4...wxt-v0.20.5)
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Don't use crypto.randUUID for shadow root UIs ([3577c0b](https://github.com/wxt-dev/wxt/commit/3577c0b))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
|
||||
## v0.20.4
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.3...wxt-v0.20.4)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Ignore popup/index.ts instead of erroring ([#1520](https://github.com/wxt-dev/wxt/pull/1520))
|
||||
- Ignore elements with a `vite-ignore` or `wxt-ignore` attribute ([#1603](https://github.com/wxt-dev/wxt/pull/1603))
|
||||
- Add `{{packageVersion}}` as template variable ([#1604](https://github.com/wxt-dev/wxt/pull/1604))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Adding missing `""` to `PublicPath` and `browser.runtime.getUrl` ([#1597](https://github.com/wxt-dev/wxt/pull/1597))
|
||||
- Fix CORS error in Firefox ([#1607](https://github.com/wxt-dev/wxt/pull/1607))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- **Content Script UI:** Add additional details about when `onRemove` is called ([656a9b3](https://github.com/wxt-dev/wxt/commit/656a9b3))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Yunsup Sim ([@SimYunSup](https://github.com/SimYunSup))
|
||||
- ТΞNSΛI ([@Tensai75](https://github.com/Tensai75))
|
||||
- Nishu ([@nishu-murmu](https://github.com/nishu-murmu))
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
|
||||
## v0.20.3
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.2...wxt-v0.20.3)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Automatically place document-level CSS outside shadow root ([#1594](https://github.com/wxt-dev/wxt/pull/1594))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Fix double hashing of inline script keys ([b0f4ac8](https://github.com/wxt-dev/wxt/commit/b0f4ac8))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron ([@aklinker1](https://github.com/aklinker1))
|
||||
|
||||
## v0.20.2
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.1...wxt-v0.20.2)
|
||||
|
||||
@@ -48,6 +48,7 @@ describe('TypeScript Project', () => {
|
||||
|
||||
declare module "wxt/browser" {
|
||||
export type PublicPath =
|
||||
| ""
|
||||
| "/"
|
||||
| "/options.html"
|
||||
| "/popup.html"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.20.2",
|
||||
"version": "0.20.6",
|
||||
"description": "⚡ Next-gen Web Extension Framework",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -16,69 +16,69 @@
|
||||
"prepack": "pnpm build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@1natsu/wait-element": "catalog:",
|
||||
"@aklinker1/rollup-plugin-visualizer": "catalog:",
|
||||
"@webext-core/fake-browser": "catalog:",
|
||||
"@webext-core/isolated-element": "catalog:",
|
||||
"@webext-core/match-patterns": "catalog:",
|
||||
"@wxt-dev/browser": "workspace:*",
|
||||
"@1natsu/wait-element": "^4.1.2",
|
||||
"@aklinker1/rollup-plugin-visualizer": "5.12.0",
|
||||
"@webext-core/fake-browser": "^1.3.2",
|
||||
"@webext-core/isolated-element": "^1.1.2",
|
||||
"@webext-core/match-patterns": "^1.0.3",
|
||||
"@wxt-dev/browser": "workspace:^",
|
||||
"@wxt-dev/storage": "workspace:^1.0.0",
|
||||
"async-mutex": "catalog:",
|
||||
"c12": "catalog:",
|
||||
"cac": "catalog:",
|
||||
"chokidar": "catalog:",
|
||||
"ci-info": "catalog:",
|
||||
"consola": "catalog:",
|
||||
"defu": "catalog:",
|
||||
"dotenv": "catalog:",
|
||||
"dotenv-expand": "catalog:",
|
||||
"esbuild": "catalog:",
|
||||
"fast-glob": "catalog:",
|
||||
"filesize": "catalog:",
|
||||
"fs-extra": "catalog:",
|
||||
"get-port-please": "catalog:",
|
||||
"giget": "catalog:",
|
||||
"hookable": "catalog:",
|
||||
"import-meta-resolve": "catalog:",
|
||||
"is-wsl": "catalog:",
|
||||
"json5": "catalog:",
|
||||
"jszip": "catalog:",
|
||||
"linkedom": "catalog:",
|
||||
"magicast": "catalog:",
|
||||
"minimatch": "catalog:",
|
||||
"nano-spawn": "catalog:",
|
||||
"normalize-path": "catalog:",
|
||||
"nypm": "catalog:",
|
||||
"ohash": "catalog:",
|
||||
"open": "catalog:",
|
||||
"ora": "catalog:",
|
||||
"perfect-debounce": "catalog:",
|
||||
"picocolors": "catalog:",
|
||||
"prompts": "catalog:",
|
||||
"publish-browser-extension": "catalog:",
|
||||
"scule": "catalog:",
|
||||
"unimport": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-node": "catalog:",
|
||||
"web-ext-run": "catalog:"
|
||||
"async-mutex": "^0.5.0",
|
||||
"c12": "^3.0.3",
|
||||
"cac": "^6.7.14",
|
||||
"chokidar": "^4.0.3",
|
||||
"ci-info": "^4.2.0",
|
||||
"consola": "^3.4.2",
|
||||
"defu": "^6.1.4",
|
||||
"dotenv": "^16.5.0",
|
||||
"dotenv-expand": "^12.0.2",
|
||||
"esbuild": "^0.25.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
"filesize": "^10.1.6",
|
||||
"fs-extra": "^11.3.0",
|
||||
"get-port-please": "^3.1.2",
|
||||
"giget": "^1.2.3 || ^2.0.0",
|
||||
"hookable": "^5.5.3",
|
||||
"import-meta-resolve": "^4.1.0",
|
||||
"is-wsl": "^3.1.0",
|
||||
"json5": "^2.2.3",
|
||||
"jszip": "^3.10.1",
|
||||
"linkedom": "^0.18.10",
|
||||
"magicast": "^0.3.5",
|
||||
"minimatch": "^10.0.1",
|
||||
"nano-spawn": "^0.2.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
"nypm": "^0.6.0",
|
||||
"ohash": "^2.0.11",
|
||||
"open": "^10.1.2",
|
||||
"ora": "^8.2.0",
|
||||
"perfect-debounce": "^1.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"prompts": "^2.4.2",
|
||||
"publish-browser-extension": "^2.3.0 || ^3.0.0",
|
||||
"scule": "^1.3.0",
|
||||
"unimport": "^3.13.1 || ^4.0.0 || ^5.0.0",
|
||||
"vite": "^5.4.19 || ^6.3.4",
|
||||
"vite-node": "^2.1.4 || ^3.1.2",
|
||||
"web-ext-run": "^0.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "catalog:",
|
||||
"@faker-js/faker": "catalog:",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/lodash.merge": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/normalize-path": "catalog:",
|
||||
"@types/prompts": "catalog:",
|
||||
"extract-zip": "catalog:",
|
||||
"happy-dom": "catalog:",
|
||||
"lodash.merge": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"publint": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"unbuild": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-plugin-random-seed": "catalog:"
|
||||
"@aklinker1/check": "2.0.0",
|
||||
"@faker-js/faker": "^9.7.0",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "^20.17.6",
|
||||
"@types/normalize-path": "^3.0.2",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"extract-zip": "^2.0.1",
|
||||
"happy-dom": "^17.4.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"oxlint": "^0.16.8",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^5.8.3",
|
||||
"unbuild": "^3.5.0",
|
||||
"vitest": "^3.1.2",
|
||||
"vitest-plugin-random-seed": "^1.1.1"
|
||||
},
|
||||
"peerDependenciesMeta": {},
|
||||
"repository": {
|
||||
@@ -167,6 +167,10 @@
|
||||
"types": "./dist/utils/match-patterns.d.ts",
|
||||
"default": "./dist/utils/match-patterns.mjs"
|
||||
},
|
||||
"./utils/split-shadow-root-css": {
|
||||
"types": "./dist/utils/split-shadow-root-css.d.ts",
|
||||
"default": "./dist/utils/split-shadow-root-css.mjs"
|
||||
},
|
||||
"./utils/storage": {
|
||||
"types": "./dist/utils/storage.d.ts",
|
||||
"default": "./dist/utils/storage.mjs"
|
||||
|
||||
@@ -85,13 +85,13 @@ export function devHtmlPrerender(
|
||||
inlineScripts.forEach((script) => {
|
||||
// Save the text content for later
|
||||
const textContent = script.textContent ?? '';
|
||||
const textHash = hash(textContent);
|
||||
inlineScriptContents[textHash] = textContent;
|
||||
const key = hash(textContent);
|
||||
inlineScriptContents[key] = textContent;
|
||||
|
||||
// Replace unsafe inline script
|
||||
const virtualScript = document.createElement('script');
|
||||
virtualScript.type = 'module';
|
||||
virtualScript.src = `${server.origin}/@id/${virtualInlineScript}?${textHash}`;
|
||||
virtualScript.src = `${server.origin}/@id/${virtualInlineScript}?${key}`;
|
||||
script.replaceWith(virtualScript);
|
||||
});
|
||||
|
||||
@@ -111,7 +111,7 @@ export function devHtmlPrerender(
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'wxt:virtualize-react-refresh',
|
||||
name: 'wxt:virtualize-inline-scripts',
|
||||
apply: 'serve',
|
||||
resolveId(id) {
|
||||
// Resolve inline scripts
|
||||
@@ -127,9 +127,9 @@ export function devHtmlPrerender(
|
||||
load(id) {
|
||||
// Resolve virtualized inline scripts
|
||||
if (id.startsWith(resolvedVirtualInlineScript)) {
|
||||
// id="virtual:wxt-inline-script?<hash>"
|
||||
const newHash = hash(id.substring(id.indexOf('?')));
|
||||
return inlineScriptContents[newHash];
|
||||
// id="virtual:wxt-inline-script?<key>"
|
||||
const key = id.substring(id.indexOf('?') + 1);
|
||||
return inlineScriptContents[key];
|
||||
}
|
||||
|
||||
// Ignore chunks during HTML file pre-rendering
|
||||
@@ -150,6 +150,13 @@ export function pointToDevServer(
|
||||
attr: string,
|
||||
) {
|
||||
document.querySelectorAll(querySelector).forEach((element) => {
|
||||
if (
|
||||
element.hasAttribute('vite-ignore') ||
|
||||
element.hasAttribute('wxt-ignore')
|
||||
) {
|
||||
element.removeAttribute('wxt-ignore');
|
||||
return;
|
||||
}
|
||||
const src = element.getAttribute(attr);
|
||||
if (!src || isUrl(src)) return;
|
||||
|
||||
|
||||
@@ -73,16 +73,18 @@ async function getPathsDeclarationEntry(
|
||||
isHtmlEntrypoint(entry) ? '.html' : '.js',
|
||||
),
|
||||
)
|
||||
.concat([''])
|
||||
.concat(await getPublicFiles());
|
||||
|
||||
await wxt.hooks.callHook('prepare:publicPaths', wxt, paths);
|
||||
|
||||
const unions = paths
|
||||
.map(normalizePath)
|
||||
.sort()
|
||||
.map((path) => ` | "/${path}"`)
|
||||
.join('\n');
|
||||
const unions = [
|
||||
` | ""`,
|
||||
` | "/"`,
|
||||
...paths
|
||||
.map(normalizePath)
|
||||
.sort()
|
||||
.map((path) => ` | "/${path}"`),
|
||||
].join('\n');
|
||||
|
||||
const template = `// Generated by wxt
|
||||
import "wxt/browser";
|
||||
|
||||
@@ -1503,7 +1503,7 @@ describe('Manifest Utils', () => {
|
||||
expect(actual).toMatchObject({
|
||||
content_security_policy:
|
||||
"script-src 'self' http://localhost:3000; object-src 'self';",
|
||||
permissions: ['http://localhost:3000/*', 'tabs'],
|
||||
permissions: ['http://localhost/*', 'tabs'],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1535,7 +1535,7 @@ describe('Manifest Utils', () => {
|
||||
sandbox:
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:3000; sandbox allow-scripts allow-forms allow-popups allow-modals; child-src 'self';",
|
||||
},
|
||||
host_permissions: ['http://localhost:3000/*'],
|
||||
host_permissions: ['http://localhost/*'],
|
||||
permissions: ['tabs', 'scripting'],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -668,27 +668,46 @@ describe('findEntrypoints', () => {
|
||||
expect(entrypoints[0]).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should not allow multiple entrypoints with the same name', async () => {
|
||||
it('should ignore other index files in the same directory when index.html exists', async () => {
|
||||
globMock.mockResolvedValueOnce([
|
||||
'content/index.ts',
|
||||
'popup/index.html',
|
||||
'popup/index.ts',
|
||||
'popup/index.css',
|
||||
]);
|
||||
|
||||
const entrypoints = await findEntrypoints();
|
||||
|
||||
expect(entrypoints).toHaveLength(2);
|
||||
expect(entrypoints[0]).toMatchObject({
|
||||
type: 'content-script',
|
||||
name: 'content',
|
||||
});
|
||||
expect(entrypoints[1]).toMatchObject({
|
||||
type: 'popup',
|
||||
name: 'popup',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow a file entrypoint and directory entrypoint to have the same name', async () => {
|
||||
globMock.mockResolvedValueOnce([
|
||||
'options/index.html',
|
||||
'options/index.jsx',
|
||||
'popup.html',
|
||||
'popup/index.html',
|
||||
'popup/index.ts',
|
||||
'ui.html',
|
||||
'other.ts',
|
||||
'other/index.ts',
|
||||
]);
|
||||
|
||||
await expect(() => findEntrypoints()).rejects.toThrowError(
|
||||
[
|
||||
'Multiple entrypoints with the same name detected, only one entrypoint for each name is allowed.',
|
||||
'',
|
||||
'- options',
|
||||
` - ${unnormalizePath('src/entrypoints/options/index.html')}`,
|
||||
` - ${unnormalizePath('src/entrypoints/options/index.jsx')}`,
|
||||
'- other',
|
||||
` - ${unnormalizePath('src/entrypoints/other.ts')}`,
|
||||
` - ${unnormalizePath('src/entrypoints/other/index.ts')}`,
|
||||
'- popup',
|
||||
` - ${unnormalizePath('src/entrypoints/popup.html')}`,
|
||||
` - ${unnormalizePath('src/entrypoints/popup/index.html')}`,
|
||||
` - ${unnormalizePath('src/entrypoints/popup/index.ts')}`,
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -53,20 +53,34 @@ export async function findEntrypoints(): Promise<Entrypoint[]> {
|
||||
relativePaths.sort();
|
||||
|
||||
const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
|
||||
const entrypointInfos: EntrypointInfo[] = relativePaths.reduce<
|
||||
EntrypointInfo[]
|
||||
>((results, relativePath) => {
|
||||
const inputPath = resolve(wxt.config.entrypointsDir, relativePath);
|
||||
const name = getEntrypointName(wxt.config.entrypointsDir, inputPath);
|
||||
const matchingGlob = pathGlobs.find((glob) =>
|
||||
minimatch(relativePath, glob),
|
||||
);
|
||||
if (matchingGlob) {
|
||||
const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
|
||||
results.push({ name, inputPath, type });
|
||||
}
|
||||
return results;
|
||||
}, []);
|
||||
const entrypointInfos: EntrypointInfo[] = relativePaths
|
||||
.reduce<EntrypointInfo[]>((results, relativePath) => {
|
||||
const inputPath = resolve(wxt.config.entrypointsDir, relativePath);
|
||||
const name = getEntrypointName(wxt.config.entrypointsDir, inputPath);
|
||||
const matchingGlob = pathGlobs.find((glob) =>
|
||||
minimatch(relativePath, glob),
|
||||
);
|
||||
if (matchingGlob) {
|
||||
const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
|
||||
results.push({ name, inputPath, type });
|
||||
}
|
||||
return results;
|
||||
}, [])
|
||||
.filter(({ name, inputPath }, _, entrypointInfos) => {
|
||||
// Remove <name>/index.* if <name>/index.html exists
|
||||
|
||||
if (inputPath.endsWith('.html')) return true;
|
||||
const isIndexFile = /index\..+$/.test(inputPath);
|
||||
if (!isIndexFile) return true;
|
||||
|
||||
const hasIndexHtml = entrypointInfos.some(
|
||||
(entry) =>
|
||||
entry.name === name && entry.inputPath.endsWith('index.html'),
|
||||
);
|
||||
if (hasIndexHtml) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
await wxt.hooks.callHook('entrypoints:found', wxt, entrypointInfos);
|
||||
|
||||
|
||||
@@ -459,7 +459,13 @@ function discoverIcons(
|
||||
}
|
||||
|
||||
function addDevModeCsp(manifest: Browser.runtime.Manifest): void {
|
||||
const permission = `${wxt.server?.origin ?? ''}/*`;
|
||||
let permissonUrl = wxt.server?.origin;
|
||||
if (permissonUrl) {
|
||||
const permissionUrlInstance = new URL(permissonUrl);
|
||||
permissionUrlInstance.port = '';
|
||||
permissonUrl = permissionUrlInstance.toString();
|
||||
}
|
||||
const permission = `${permissonUrl}*`;
|
||||
const allowedCsp = wxt.server?.origin ?? 'http://localhost:*';
|
||||
|
||||
if (manifest.manifest_version === 3) {
|
||||
|
||||
@@ -26,11 +26,10 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
|
||||
wxt.logger.info('Zipping extension...');
|
||||
const zipFiles: string[] = [];
|
||||
|
||||
const packageJson = await getPackageJson();
|
||||
const projectName =
|
||||
wxt.config.zip.name ??
|
||||
safeFilename(
|
||||
(await getPackageJson())?.name || path.basename(process.cwd()),
|
||||
);
|
||||
safeFilename(packageJson?.name || path.basename(process.cwd()));
|
||||
const applyTemplate = (template: string): string =>
|
||||
template
|
||||
.replaceAll('{{name}}', projectName)
|
||||
@@ -39,6 +38,7 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
|
||||
'{{version}}',
|
||||
output.manifest.version_name ?? output.manifest.version,
|
||||
)
|
||||
.replaceAll('{{packageVersion}}', packageJson?.version)
|
||||
.replaceAll('{{mode}}', wxt.config.mode)
|
||||
.replaceAll('{{manifestVersion}}', `mv${wxt.config.manifestVersion}`);
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ export interface InlineConfig {
|
||||
*
|
||||
* - <span v-pre>`{{name}}`</span> - The project's name converted to kebab-case
|
||||
* - <span v-pre>`{{version}}`</span> - The version_name or version from the manifest
|
||||
* - <span v-pre>`{{packageVersion}}`</span> - The version from the package.json
|
||||
* - <span v-pre>`{{browser}}`</span> - The target browser from the `--browser` CLI flag
|
||||
* - <span v-pre>`{{mode}}`</span> - The current mode
|
||||
* - <span v-pre>`{{manifestVersion}}`</span> - Either "2" or "3"
|
||||
@@ -171,6 +172,7 @@ export interface InlineConfig {
|
||||
*
|
||||
* - <span v-pre>`{{name}}`</span> - The project's name converted to kebab-case
|
||||
* - <span v-pre>`{{version}}`</span> - The version_name or version from the manifest
|
||||
* - <span v-pre>`{{packageVersion}}`</span> - The version from the package.json
|
||||
* - <span v-pre>`{{browser}}`</span> - The target browser from the `--browser` CLI flag
|
||||
* - <span v-pre>`{{mode}}`</span> - The current mode
|
||||
* - <span v-pre>`{{manifestVersion}}`</span> - Either "2" or "3"
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ContentScriptUi, ContentScriptUiOptions } from './types';
|
||||
import { createIsolatedElement } from '@webext-core/isolated-element';
|
||||
import { applyPosition, createMountFunctions, mountUi } from './shared';
|
||||
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).
|
||||
@@ -17,10 +18,11 @@ export async function createShadowRootUi<TMounted>(
|
||||
ctx: ContentScriptContext,
|
||||
options: ShadowRootContentScriptUiOptions<TMounted>,
|
||||
): Promise<ShadowRootContentScriptUi<TMounted>> {
|
||||
const instanceId = Math.random().toString(36).substring(2, 15);
|
||||
const css: string[] = [];
|
||||
|
||||
if (!options.inheritStyles) {
|
||||
css.push(`/* WXT Shadow Root Reset */ body{all:initial;}`);
|
||||
css.push(`/* WXT Shadow Root Reset */ :host{all:initial !important;}`);
|
||||
}
|
||||
if (options.css) {
|
||||
css.push(options.css);
|
||||
@@ -31,6 +33,9 @@ export async function createShadowRootUi<TMounted>(
|
||||
css.push(entryCss.replaceAll(':root', ':host'));
|
||||
}
|
||||
|
||||
// Some rules must be applied outside the shadow root, so split the CSS apart
|
||||
const { shadowCss, documentCss } = splitShadowRootCss(css.join('\n').trim());
|
||||
|
||||
const {
|
||||
isolatedElement: uiContainer,
|
||||
parentElement: shadowHost,
|
||||
@@ -38,7 +43,7 @@ export async function createShadowRootUi<TMounted>(
|
||||
} = await createIsolatedElement({
|
||||
name: options.name,
|
||||
css: {
|
||||
textContent: css.join('\n').trim(),
|
||||
textContent: shadowCss,
|
||||
},
|
||||
mode: options.mode ?? 'open',
|
||||
isolateEvents: options.isolateEvents,
|
||||
@@ -51,6 +56,20 @@ export async function createShadowRootUi<TMounted>(
|
||||
// Add shadow root element to DOM
|
||||
mountUi(shadowHost, options);
|
||||
applyPosition(shadowHost, shadow.querySelector('html'), options);
|
||||
|
||||
// Add document CSS
|
||||
if (
|
||||
documentCss &&
|
||||
!document.querySelector(
|
||||
`style[wxt-shadow-root-document-styles="${instanceId}"]`,
|
||||
)
|
||||
) {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = documentCss;
|
||||
style.setAttribute('wxt-shadow-root-document-styles', instanceId);
|
||||
(document.head ?? document.body).append(style);
|
||||
}
|
||||
|
||||
// Mount UI inside shadow root
|
||||
mounted = options.onMount(uiContainer, shadow, shadowHost);
|
||||
};
|
||||
@@ -58,11 +77,20 @@ export async function createShadowRootUi<TMounted>(
|
||||
const remove = () => {
|
||||
// Cleanup mounted state
|
||||
options.onRemove?.(mounted);
|
||||
|
||||
// Detach shadow root from DOM
|
||||
shadowHost.remove();
|
||||
|
||||
// Remove document CSS
|
||||
const documentStyle = document.querySelector(
|
||||
`style[wxt-shadow-root-document-styles="${instanceId}"]`,
|
||||
);
|
||||
documentStyle?.remove();
|
||||
|
||||
// Remove children from uiContainer
|
||||
while (uiContainer.lastChild)
|
||||
uiContainer.removeChild(uiContainer.lastChild);
|
||||
|
||||
// Clear mounted value
|
||||
mounted = undefined;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,9 @@ export type ContentScriptUiOptions<TMounted> = ContentScriptPositioningOptions &
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
onRemove?: (mounted: TMounted | undefined) => void;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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): {
|
||||
documentCss: string;
|
||||
shadowCss: string;
|
||||
} {
|
||||
let shadowCss = css;
|
||||
let documentCss = '';
|
||||
|
||||
const rulesRegex = /(\s*@(property|font-face)[\s\S]*?{[\s\S]*?})/gm;
|
||||
let match;
|
||||
while ((match = rulesRegex.exec(css)) !== null) {
|
||||
documentCss += match[1];
|
||||
shadowCss = shadowCss.replace(match[1], '');
|
||||
}
|
||||
|
||||
return {
|
||||
documentCss: documentCss.trim(),
|
||||
shadowCss: shadowCss.trim(),
|
||||
};
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
"src/utils/define-unlisted-script.ts",
|
||||
"src/utils/define-wxt-plugin.ts",
|
||||
"src/utils/match-patterns.ts",
|
||||
"src/utils/split-shadow-root-css.ts",
|
||||
"src/utils/storage.ts",
|
||||
"src/testing/index.ts",
|
||||
"src/modules.ts"
|
||||
|
||||
@@ -8,102 +8,3 @@ onlyBuiltDependencies:
|
||||
- sharp
|
||||
- simple-git-hooks
|
||||
- spawn-sync
|
||||
catalog:
|
||||
'@1natsu/wait-element': ^4.1.2
|
||||
'@aklinker1/buildc': ^1.1.4
|
||||
'@aklinker1/check': ^1.4.5
|
||||
'@aklinker1/rollup-plugin-visualizer': 5.12.0
|
||||
'@commitlint/config-conventional': ^19.8.0
|
||||
'@commitlint/types': ^19.8.0
|
||||
'@faker-js/faker': ^9.6.0
|
||||
'@sveltejs/vite-plugin-svelte': ^4.0.0 || ^5.0.0
|
||||
'@types/chrome': ^0.0.313
|
||||
'@types/fs-extra': ^11.0.4
|
||||
'@types/lodash.merge': ^4.6.9
|
||||
'@types/node': ^20.17.6
|
||||
'@types/normalize-path': ^3.0.2
|
||||
'@types/prompts': ^2.4.9
|
||||
'@types/react': ^19.1.0
|
||||
'@types/react-dom': ^19.1.2
|
||||
'@types/ua-parser-js': ^0.7.39
|
||||
'@types/webextension-polyfill': ^0.12.3
|
||||
'@vitejs/plugin-react': ^4.3.4
|
||||
'@vitejs/plugin-vue': ^5.2.3
|
||||
'@vitest/coverage-v8': ^3.1.1
|
||||
'@webext-core/fake-browser': ^1.3.2
|
||||
'@webext-core/isolated-element': ^1.1.2
|
||||
'@webext-core/match-patterns': ^1.0.3
|
||||
async-mutex: ^0.5.0
|
||||
c12: ^3.0.3
|
||||
cac: ^6.7.14
|
||||
changelogen: ^0.6.1
|
||||
chokidar: ^4.0.3
|
||||
ci-info: ^4.2.0
|
||||
confbox: ^0.1.8 || ^0.2.2
|
||||
consola: ^3.4.2
|
||||
defu: ^6.1.4
|
||||
dequal: ^2.0.3
|
||||
dotenv: ^16.4.7
|
||||
dotenv-expand: ^12.0.1
|
||||
esbuild: ^0.25.0
|
||||
extract-zip: ^2.0.1
|
||||
fast-glob: ^3.3.3
|
||||
feed: ^4.2.2
|
||||
filesize: ^10.1.6
|
||||
fs-extra: ^11.3.0
|
||||
get-port-please: ^3.1.2
|
||||
giget: ^1.2.3 || ^2.0.0
|
||||
happy-dom: ^17.4.4
|
||||
hookable: ^5.5.3
|
||||
import-meta-resolve: ^4.1.0
|
||||
is-wsl: ^3.1.0
|
||||
json5: ^2.2.3
|
||||
jszip: ^3.10.1
|
||||
linkedom: ^0.18.9
|
||||
lint-staged: ^15.5.0
|
||||
lodash.merge: ^4.6.2
|
||||
magicast: ^0.3.5
|
||||
markdown-it-footnote: ^4.0.0
|
||||
minimatch: ^10.0.1
|
||||
nano-spawn: ^0.2.0
|
||||
normalize-path: ^3.0.0
|
||||
nypm: ^0.6.0
|
||||
ohash: ^2.0.11
|
||||
open: ^10.1.0
|
||||
ora: ^8.2.0
|
||||
oxlint: ^0.16.5
|
||||
perfect-debounce: ^1.0.0
|
||||
picocolors: ^1.1.1
|
||||
prettier: ^3.5.3
|
||||
prompts: ^2.4.2
|
||||
publint: ^0.3.10
|
||||
publish-browser-extension: ^2.3.0 || ^3.0.0
|
||||
react: ^19.1.0
|
||||
react-dom: ^19.1.0
|
||||
sass: ^1.86.3
|
||||
scule: ^1.3.0
|
||||
sharp: ^0.34.1
|
||||
simple-git-hooks: ^2.12.1
|
||||
solid-js: ^1.9.5
|
||||
tsx: 4.19.3
|
||||
typedoc: ^0.25.4
|
||||
typedoc-plugin-frontmatter: ^1.3.0
|
||||
typedoc-plugin-markdown: 4.0.0-next.23
|
||||
typedoc-vitepress-theme: 1.0.0-next.3
|
||||
typescript: ^5.8.3
|
||||
ua-parser-js: ^1.0.40
|
||||
unbuild: ^3.5.0
|
||||
unimport: ^3.13.1 || ^4.0.0 || ^5.0.0
|
||||
unocss: ^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0
|
||||
vite: ^5.4.17 || ^6.2.5
|
||||
vite-node: ^2.1.4 || ^3.0.0
|
||||
vite-plugin-solid: ^2.11.6
|
||||
vitepress: ^1.6.3
|
||||
vitepress-knowledge: ^0.4.1
|
||||
vitepress-plugin-group-icons: ^1.4.1
|
||||
vitest: ^3.1.1
|
||||
vitest-mock-extended: ^3.1.0
|
||||
vitest-plugin-random-seed: ^1.1.1
|
||||
vue: ^3.5.13
|
||||
web-ext-run: ^0.2.2
|
||||
webextension-polyfill: ^0.12.0
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.3",
|
||||
"@wxt-dev/module-react": "^1.1.3",
|
||||
"typescript": "^5.8.3",
|
||||
"wxt": "^0.20.0"
|
||||
"wxt": "^0.20.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
"postinstall": "wxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"solid-js": "^1.9.5"
|
||||
"solid-js": "^1.9.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wxt-dev/module-solid": "^1.1.3",
|
||||
"typescript": "^5.8.3",
|
||||
"wxt": "^0.20.0"
|
||||
"wxt": "^0.20.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
"devDependencies": {
|
||||
"@tsconfig/svelte": "^5.0.4",
|
||||
"@wxt-dev/module-svelte": "^2.0.3",
|
||||
"svelte": "^5.25.9",
|
||||
"svelte-check": "^4.1.5",
|
||||
"svelte": "^5.28.2",
|
||||
"svelte-check": "^4.1.6",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.8.3",
|
||||
"wxt": "^0.20.0"
|
||||
"wxt": "^0.20.6"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 559 B |
|
Before Width: | Height: | Size: 916 B After Width: | Height: | Size: 916 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -16,6 +16,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.8.3",
|
||||
"wxt": "^0.20.0"
|
||||
"wxt": "^0.20.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"devDependencies": {
|
||||
"@wxt-dev/module-vue": "^1.0.2",
|
||||
"typescript": "5.6.3",
|
||||
"vue-tsc": "^2.2.8",
|
||||
"wxt": "^0.20.0"
|
||||
"vue-tsc": "^2.2.10",
|
||||
"wxt": "^0.20.6"
|
||||
}
|
||||
}
|
||||
|
||||