Compare commits

...

7 Commits

Author SHA1 Message Date
github-actions[bot] 0b5424cadb chore(release): @wxt-dev/module-svelte v2.0.5
📼 VHS / Create VHS (push) Cancelled after 0s
2026-03-19 14:06:34 +00:00
Aaron 48571f5032 fix: Update @sveltejs/vite-plugin-svelte version range to support Vite
8
2026-03-19 09:01:44 -05:00
Aaron c6c9f65040 chore: Remove any from i18n's internal types (#2201)
Co-authored-by: Patryk Kuniczak <p.kuniczak@gmail.com>
2026-03-19 09:22:15 +01:00
Harryson ace6c20164 feat: replace dotenv with native node:util parseEnv (#2181)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-03-18 15:04:45 +00:00
Aditi d64b64f39e chore: replace minimatch with picomatch (#2188)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
Co-authored-by: Rizky Mirzaviandy Priambodo <Xavrir@users.noreply.github.com>
2026-03-18 14:22:38 +00:00
Aaron 41a687a63e docs: Add note about adding the browser package as a dependency for
module augmentation
2026-03-17 17:40:48 -05:00
Aaron f4f6704510 docs: Document augmenting the browser type for Firefox (#2199) 2026-03-17 08:44:33 -05:00
13 changed files with 208 additions and 95 deletions
+22
View File
@@ -76,6 +76,28 @@ Alternatively, if you're trying to use similar APIs under different names (to su
});
```
### Augmenting the Browser Type
WXT's `browser` types are based on the `@types/chrome` package. That means some Firefox-specific APIs may not be typed, like `browser.sidebarAction`. If you want to add types for these APIs, you can augment the browser type to add them yourself:
```ts
// <srcDir>/browser-types.d.ts
import '@wxt-dev/browser';
import type { SidebarAction } from 'webextension-polyfill';
declare module '@wxt-dev/browser' {
namespace Browser {
export const sidebarAction: SidebarAction.Static;
}
}
```
> For this to work, you may need to install `@wxt-dev/browser` as a direct dependency.
>
> ```sh
> pnpm add @wxt-dev/browser
> ```
## Entrypoint Limitations
Because WXT imports your entrypoint files into a NodeJS, non-extension environment, the `chrome`/`browser` variables provided to extensions by the browser **will not be available**.
+25 -7
View File
@@ -16,12 +16,25 @@ export { SUPPORTED_LOCALES } from './supported-locales';
// TYPES
//
export type SimpleMessage = string;
export type PluralMessage = Record<'n' | number, string>;
export interface ChromeMessage {
message: string;
description?: string;
placeholders?: Record<string, { content: string; example?: string }>;
}
export type Message =
| SimpleMessage
| PluralMessage
| ChromeMessage
| Message[]
| { [key: string]: Message };
export type MessagesObject = Record<string, Message>;
export interface ParsedBaseMessage {
key: string[];
substitutions: number;
@@ -95,7 +108,7 @@ const EXT_FORMATS_MAP: Record<string, MessageFormat> = {
'.toml': 'TOML',
};
const PARSERS: Record<MessageFormat, (text: string) => any> = {
const PARSERS: Record<MessageFormat, (text: string) => MessagesObject> = {
YAML: parseYAML,
JSON5: parseJSON5,
TOML: parseTOML,
@@ -117,6 +130,7 @@ export async function parseMessagesFile(
): Promise<ParsedMessage[]> {
const text = await readFile(file, 'utf8');
const ext = extname(file).toLowerCase();
return parseMessagesText(text, EXT_FORMATS_MAP[ext] ?? 'JSON5');
}
@@ -129,7 +143,7 @@ export function parseMessagesText(
}
/** Given the JS object form of a raw messages file, extract the messages. */
export function parseMessagesObject(object: any): ParsedMessage[] {
export function parseMessagesObject(object: MessagesObject): ParsedMessage[] {
return _parseMessagesObject(
[],
{
@@ -142,7 +156,7 @@ export function parseMessagesObject(object: any): ParsedMessage[] {
function _parseMessagesObject(
path: string[],
object: any,
object: Message,
depth: number,
): ParsedMessage[] {
switch (typeof object) {
@@ -163,15 +177,17 @@ function _parseMessagesObject(
];
}
case 'object':
if ([null, undefined].includes(object)) {
if (object == null) {
throw new Error(
`Messages file should not contain \`${object}\` (found at "${path.join('.')}")`,
);
}
if (Array.isArray(object))
return object.flatMap((item, i) =>
_parseMessagesObject(path.concat(String(i)), item, depth + 1),
);
if (isPluralMessage(object)) {
const message = Object.values(object).join('|');
const substitutions = getSubstitutionCount(message);
@@ -184,6 +200,7 @@ function _parseMessagesObject(
},
];
}
if (depth === 1 && isChromeMessage(object)) {
const message = applyChromeMessagePlaceholders(object);
const substitutions = getSubstitutionCount(message);
@@ -196,6 +213,7 @@ function _parseMessagesObject(
},
];
}
return Object.entries(object).flatMap(([key, value]) =>
_parseMessagesObject(path.concat(key), value, depth + 1),
);
@@ -204,13 +222,13 @@ function _parseMessagesObject(
}
}
function isPluralMessage(object: any): object is Record<number | 'n', string> {
function isPluralMessage(object: Message): object is PluralMessage {
return Object.keys(object).every(
(key) => key === 'n' || isFinite(Number(key)),
);
}
function isChromeMessage(object: any): object is ChromeMessage {
function isChromeMessage(object: Message): object is ChromeMessage {
return Object.keys(object).every((key) =>
ALLOWED_CHROME_MESSAGE_KEYS.has(key),
);
@@ -222,7 +240,7 @@ function isChromeMessage(object: any): object is ChromeMessage {
export function generateTypeText(messages: ParsedMessage[]): string {
const renderMessageEntry = (message: ParsedMessage): string => {
// Use . for deep keys at runtime and types
// Use '.' for deep keys at runtime and types
const key = message.key.join('.');
const features = [
+21
View File
@@ -1,5 +1,26 @@
# Changelog
## v2.0.5
[compare changes](https://github.com/wxt-dev/wxt/compare/module-svelte-v2.0.4...module-svelte-v2.0.5)
### 🩹 Fixes
- Update `@sveltejs/vite-plugin-svelte` version range to support Vite 8 ([48571f50](https://github.com/wxt-dev/wxt/commit/48571f50))
### 🏡 Chore
- Upgrade dev and non-major prod dependencies ([#2000](https://github.com/wxt-dev/wxt/pull/2000))
- Use `tsdown` to build packages ([#2006](https://github.com/wxt-dev/wxt/pull/2006))
- Move script-only dev dependencies to top-level `package.json` ([#2007](https://github.com/wxt-dev/wxt/pull/2007))
- Update dependencies ([#2069](https://github.com/wxt-dev/wxt/pull/2069))
- **deps:** Upgrade deps ([#2175](https://github.com/wxt-dev/wxt/pull/2175))
- Add prepack script to all packages ([032f7931](https://github.com/wxt-dev/wxt/commit/032f7931))
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
## v2.0.4
[compare changes](https://github.com/wxt-dev/wxt/compare/module-svelte-v2.0.3...module-svelte-v2.0.4)
+2 -2
View File
@@ -18,7 +18,7 @@
},
"license": "MIT",
"funding": "https://github.com/sponsors/wxt-dev",
"version": "2.0.4",
"version": "2.0.5",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -48,7 +48,7 @@
"svelte": ">=5"
},
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^4.0.0 || ^5.0.0 || ^6.0.0"
"@sveltejs/vite-plugin-svelte": ">=4"
},
"devDependencies": {
"publint": "^0.3.18",
+2 -2
View File
@@ -30,7 +30,6 @@
"ci-info": "^4.4.0",
"consola": "^3.4.2",
"defu": "^6.1.4",
"dotenv": "^17.3.1",
"dotenv-expand": "^12.0.3",
"esbuild": "^0.27.1",
"filesize": "^11.0.13",
@@ -43,7 +42,6 @@
"jszip": "^3.10.1",
"linkedom": "^0.18.12",
"magicast": "^0.5.2",
"minimatch": "^10.2.4",
"nano-spawn": "^2.0.0",
"nanospinner": "^1.2.2",
"normalize-path": "^3.0.0",
@@ -52,6 +50,7 @@
"open": "^11.0.0",
"perfect-debounce": "^2.1.0",
"picocolors": "^1.1.1",
"picomatch": "^4.0.3",
"prompts": "^2.4.2",
"publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.4",
"scule": "^1.3.0",
@@ -74,6 +73,7 @@
"@types/lodash.merge": "^4.6.9",
"@types/node": "^20.17.6",
"@types/normalize-path": "^3.0.2",
"@types/picomatch": "^4.0.2",
"@types/prompts": "^2.4.9",
"eslint": "^10.0.0",
"extract-zip": "^2.0.1",
@@ -1,46 +0,0 @@
import { describe, it, expect } from 'vitest';
import { minimatchMultiple } from '../minimatch-multiple';
describe('minimatchMultiple', () => {
it('should return false if the pattern array is undefined', () => {
const patterns = undefined;
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern array is empty', () => {
const patterns: string[] = [];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return true if the pattern array contains a match', () => {
const patterns = ['test.yml', 'test.json'];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(true);
});
it('should return false if the pattern array does not contain a match', () => {
const patterns = ['test.yml', 'test.json'];
const search = 'test.txt';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern matches a negative pattern', () => {
const patterns = ['test.*', '!test.json'];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern matches a negative pattern, regardless of order', () => {
const patterns = ['!test.json', 'test.*'];
const search = 'test.json';
expect(minimatchMultiple(search, patterns)).toBe(false);
});
});
@@ -0,0 +1,71 @@
import { describe, it, expect } from 'vitest';
import { picomatchMultiple } from '../picomatch-multiple';
describe('picomatchMultiple', () => {
it('should return false if the pattern array is undefined', () => {
const patterns = undefined;
const search = 'test.json';
expect(picomatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern array is empty', () => {
const patterns: string[] = [];
const search = 'test.json';
expect(picomatchMultiple(search, patterns)).toBe(false);
});
it('should return true if the pattern array contains a match', () => {
const patterns = ['test.yml', 'test.json'];
const search = 'test.json';
expect(picomatchMultiple(search, patterns)).toBe(true);
});
it('should return false if the pattern array does not contain a match', () => {
const patterns = ['test.yml', 'test.json'];
const search = 'test.txt';
expect(picomatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern matches a negative pattern', () => {
const patterns = ['test.*', '!test.json'];
const search = 'test.json';
expect(picomatchMultiple(search, patterns)).toBe(false);
});
it('should return false if the pattern matches a negative pattern, regardless of order', () => {
const patterns = ['!test.json', 'test.*'];
const search = 'test.json';
expect(picomatchMultiple(search, patterns)).toBe(false);
});
it('should support extglob-like extension matching', () => {
const patterns = ['content.[jt]s?(x)'];
expect(picomatchMultiple('content.ts', patterns)).toBe(true);
expect(picomatchMultiple('content.jsx', patterns)).toBe(true);
expect(picomatchMultiple('content.css', patterns)).toBe(false);
});
it('should support nested paths', () => {
const patterns = ['foo/**/*.ts'];
expect(picomatchMultiple('foo/bar/baz.ts', patterns)).toBe(true);
expect(picomatchMultiple('foo/bar/baz.js', patterns)).toBe(false);
});
it('should preserve include/exclude interaction used by zip filtering', () => {
const include = ['special.txt'];
const exclude = ['**/*.txt'];
const search = 'special.txt';
const shouldInclude =
picomatchMultiple(search, include) || !picomatchMultiple(search, exclude);
expect(shouldInclude).toBe(true);
});
});
@@ -13,7 +13,7 @@ import {
UnlistedScriptEntrypoint,
} from '../../../types';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { minimatch } from 'minimatch';
import picomatch from 'picomatch';
import { parseHTML } from 'linkedom';
import JSON5 from 'json5';
import { glob } from 'tinyglobby';
@@ -61,7 +61,7 @@ export async function findEntrypoints(): Promise<Entrypoint[]> {
const inputPath = resolve(wxt.config.entrypointsDir, relativePath);
const name = getEntrypointName(wxt.config.entrypointsDir, inputPath);
const matchingGlob = pathGlobs.find((glob) =>
minimatch(relativePath, glob),
picomatch(glob)(relativePath),
);
if (matchingGlob) {
const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
+37 -15
View File
@@ -1,23 +1,45 @@
import { config } from 'dotenv';
import { readFileSync, existsSync } from 'node:fs';
import { parseEnv } from 'node:util';
import { expand } from 'dotenv-expand';
import type { TargetBrowser } from '../../types';
/** Load environment files based on the current mode and browser. */
export function loadEnv(mode: string, browser: TargetBrowser) {
return expand(
config({
quiet: true,
// Files on top override files below
path: [
`.env.${mode}.${browser}.local`,
`.env.${mode}.${browser}`,
`.env.${browser}.local`,
`.env.${browser}`,
`.env.${mode}.local`,
`.env.${mode}`,
`.env.local`,
`.env`,
],
const envFiles = [
// List is ordered with general files first, specific ones last, so the more
// specific files override the more general ones in the loop below.
`.env`,
`.env.local`,
`.env.${mode}`,
`.env.${mode}.local`,
`.env.${browser}`,
`.env.${browser}.local`,
`.env.${mode}.${browser}`,
`.env.${mode}.${browser}.local`,
];
const parsed = Object.fromEntries<string>(
envFiles.flatMap((filePath) => {
if (!existsSync(filePath)) return [];
try {
const content = readFileSync(filePath, 'utf-8');
const parsedEnv = parseEnv(content);
return Object.entries(parsedEnv) as [string, string][];
} catch {
return [];
}
}),
);
// Make a copy of `process.env` so that `dotenv-expand` doesn't re-assign the
// expanded values to the global `process.env`.
const processEnv = { ...process.env } as Record<string, string>;
expand({
parsed,
processEnv,
});
return parsed;
}
@@ -1,7 +1,7 @@
import { minimatch, MinimatchOptions } from 'minimatch';
import picomatch, { PicomatchOptions } from 'picomatch';
/**
* Run [`minimatch`](https://npmjs.com/package/minimatch) against multiple
* Run [`picomatch`](https://npmjs.com/package/picomatch) against multiple
* patterns.
*
* Supports negated patterns, the order does not matter. If your `search` string
@@ -9,14 +9,14 @@ import { minimatch, MinimatchOptions } from 'minimatch';
*
* @example
* ```ts
* minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
* minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
* picomatchMultiple('a.json', ['*.json', '!b.json']); // => true
* picomatchMultiple('b.json', ['*.json', '!b.json']); // => false
* ```;
*/
export function minimatchMultiple(
export function picomatchMultiple(
search: string,
patterns: string[] | undefined,
options?: MinimatchOptions,
options?: PicomatchOptions,
): boolean {
if (patterns == null) return false;
@@ -29,12 +29,12 @@ export function minimatchMultiple(
if (
negatePatterns.some((negatePattern) =>
minimatch(search, negatePattern, options),
picomatch(negatePattern, options)(search),
)
)
return false;
return positivePatterns.some((positivePattern) =>
minimatch(search, positivePattern, options),
picomatch(positivePattern, options)(search),
);
}
+3 -3
View File
@@ -11,7 +11,7 @@ import { registerWxt, wxt } from './wxt';
import JSZip from 'jszip';
import { glob } from 'tinyglobby';
import { normalizePath } from './utils';
import { minimatchMultiple } from './utils/minimatch-multiple';
import { picomatchMultiple } from './utils/picomatch-multiple';
/**
* Build and zip the extension for distribution.
@@ -125,8 +125,8 @@ async function zipDir(
})
).filter((relativePath) => {
return (
minimatchMultiple(relativePath, options?.include) ||
!minimatchMultiple(relativePath, options?.exclude)
picomatchMultiple(relativePath, options?.include) ||
!picomatchMultiple(relativePath, options?.exclude)
);
});
const filesToZip = [
+3 -3
View File
@@ -212,7 +212,7 @@ export interface InlineConfig {
*/
sourcesRoot?: string;
/**
* [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to
* [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to
* include when creating a ZIP of all your source code for Firefox. Patterns
* are relative to your `config.zip.sourcesRoot`.
*
@@ -226,7 +226,7 @@ export interface InlineConfig {
*/
includeSources?: string[];
/**
* [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to
* [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to
* exclude when creating a ZIP of all your source code for Firefox. Patterns
* are relative to your `config.zip.sourcesRoot`.
*
@@ -239,7 +239,7 @@ export interface InlineConfig {
*/
excludeSources?: string[];
/**
* [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to
* [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to
* exclude when zipping the extension.
*
* @example
+12 -7
View File
@@ -297,7 +297,7 @@ importers:
packages/module-svelte:
dependencies:
'@sveltejs/vite-plugin-svelte':
specifier: ^4.0.0 || ^5.0.0 || ^6.0.0
specifier: '>=4'
version: 6.2.4(vite@7.3.1(@types/node@20.19.32)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))
devDependencies:
publint:
@@ -456,9 +456,6 @@ importers:
defu:
specifier: ^6.1.4
version: 6.1.4
dotenv:
specifier: ^17.3.1
version: 17.3.1
dotenv-expand:
specifier: ^12.0.3
version: 12.0.3
@@ -495,9 +492,6 @@ importers:
magicast:
specifier: ^0.5.2
version: 0.5.2
minimatch:
specifier: ^10.2.4
version: 10.2.4
nano-spawn:
specifier: ^2.0.0
version: 2.0.0
@@ -522,6 +516,9 @@ importers:
picocolors:
specifier: ^1.1.1
version: 1.1.1
picomatch:
specifier: ^4.0.3
version: 4.0.3
prompts:
specifier: ^2.4.2
version: 2.4.2
@@ -559,6 +556,9 @@ importers:
'@types/normalize-path':
specifier: ^3.0.2
version: 3.0.2
'@types/picomatch':
specifier: ^4.0.2
version: 4.0.2
'@types/prompts':
specifier: ^2.4.9
version: 2.4.9
@@ -2195,6 +2195,9 @@ packages:
'@types/normalize-path@3.0.2':
resolution: {integrity: sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==}
'@types/picomatch@4.0.2':
resolution: {integrity: sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==}
'@types/prompts@2.4.9':
resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==}
@@ -6299,6 +6302,8 @@ snapshots:
'@types/normalize-path@3.0.2': {}
'@types/picomatch@4.0.2': {}
'@types/prompts@2.4.9':
dependencies:
'@types/node': 20.19.32