Compare commits

..

21 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
github-actions[bot] 65fc0fadb3 chore(release): wxt v0.20.20
📼 VHS / Create VHS (push) Cancelled after 0s
2026-03-17 13:21:45 +00:00
Aaron 64b686713b chore(deps): Add vite-node 6 support 2026-03-17 08:16:33 -05:00
Joseph Hu 9c6d3e37d4 docs: Add Extension Rank Checker to the list of extensions (#2193)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-03-17 08:00:23 -05:00
FJRG2007 ツ 37abe9db39 docs: Add Dymo extension ID to UsingWxtSection.vue (#2187)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-03-17 12:43:08 +00:00
Yue Yang 9ebef4a268 chore: remove useDefineForClassFields from svelte/tsconfig.json (#2190) 2026-03-17 07:37:38 -05:00
Hampus Tågerud e60f7c7e77 fix: unlisted script return values broken with Vite 8 sourcemaps (#2197) 2026-03-17 12:35:22 +00:00
aklinker1 36aadd0604 fix: Upgrade @wxt-dev/browser to latest @types/chrome version 2026-03-17 00:39:30 +00:00
github-actions[bot] e4598a5e37 chore(release): @wxt-dev/module-react v1.2.2
📼 VHS / Create VHS (push) Cancelled after 0s
2026-03-14 23:48:01 +00:00
github-actions[bot] db5c22809c chore(release): wxt v0.20.19
📼 VHS / Create VHS (push) Cancelled after 0s
2026-03-14 23:43:04 +00:00
Aaron c61fa8f858 fix: Add @vitejs/plugin-react v6 support 2026-03-14 18:38:33 -05:00
Florian Metz 5fe4681620 chore: replace fs-extra with node:fs/promises (#2174)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
2026-03-06 09:29:49 -06:00
Aaron 80d384361b chore: Update nano-staged precommit command 2026-03-06 09:17:22 -06:00
dagangtj a1e72c0749 chore: replace lint-staged with nano-staged (#2179)
Co-authored-by: Dean <dagangtj@gmail.com>
2026-03-06 15:13:41 +00:00
Aaron e3a80b8247 ci: Re-enable react template validation 2026-03-06 08:54:08 -06:00
62 changed files with 637 additions and 369 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ jobs:
fail-fast: false
matrix:
template:
# - react
- react
- solid
- svelte
- vanilla
@@ -121,6 +121,8 @@ const chromeExtensionIds = [
'lapnciffpekdengooeolaienkeoilfeo', // All API Hub AI Relay & New API Manager - https://github.com/qixing-jk/all-api-hub
'bhgobenflkkhfcgkikejaaejenoddcmo', // Scrape Similar - Extract data from websites into spreadsheets - https://github.com/zizzfizzix/scrape-similar
'kinlknncggaihnhdcalijdmpbhbflalm', // isTrust - https://github.com/Internet-Society-Belgium/isTrust/
'ojpakgiekphppgkcdihbjpafobhnhlkp', // Dymo
'pmgehhllikbjmadpenhabejhpemplhmd', // Extension Rank Checker - Extension Ranker
];
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
+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**.
+3 -5
View File
@@ -20,17 +20,15 @@
"@aklinker1/check": "^2.2.0",
"@commitlint/config-conventional": "^20.4.3",
"@commitlint/types": "^20.4.3",
"@types/fs-extra": "^11.0.4",
"@types/semver": "^7.7.1",
"@vitest/coverage-v8": "^4.0.18",
"changelogen": "^0.6.2",
"consola": "^3.4.2",
"feed": "^5.2.0",
"fs-extra": "^11.3.4",
"lint-staged": "^16.3.2",
"markdown-it-footnote": "^4.0.0",
"markdownlint-cli": "^0.48.0",
"nano-spawn": "^2.0.0",
"nano-staged": "^0.8.0",
"p-map": "^7.0.4",
"prettier": "^3.8.1",
"prettier-plugin-jsdoc": "^1.8.0",
@@ -53,9 +51,9 @@
"wxt": "workspace:*"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
"pre-commit": "./node_modules/.bin/nano-staged"
},
"lint-staged": {
"nano-staged": {
"*": "prettier --ignore-unknown --write"
}
}
-1
View File
@@ -48,7 +48,6 @@
},
"dependencies": {
"defu": "^6.1.4",
"fs-extra": "^11.3.4",
"sharp": "^0.34.5"
},
"devDependencies": {
+11 -10
View File
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { resolve } from 'node:path';
import * as fsExtra from 'fs-extra';
import * as fsPromises from 'node:fs/promises';
import sharp from 'sharp';
import type { Wxt, UserManifest } from 'wxt';
@@ -9,9 +9,9 @@ import autoIconsModule from '../index';
import type { AutoIconsOptions } from '../index';
// Mock dependencies
vi.mock('fs-extra', () => ({
ensureDir: vi.fn(),
pathExists: vi.fn(),
vi.mock('node:fs/promises', () => ({
mkdir: vi.fn(),
access: vi.fn(),
}));
vi.mock('sharp', () => ({
@@ -86,8 +86,8 @@ describe('auto-icons module', () => {
vi.mocked(sharp).mockReturnValue(
mockSharpInstance as unknown as sharp.Sharp,
);
vi.mocked(fsExtra.pathExists).mockResolvedValue(true as any);
vi.mocked(fsExtra.ensureDir).mockResolvedValue(undefined as any);
vi.mocked(fsPromises.access).mockResolvedValue(undefined);
vi.mocked(fsPromises.mkdir).mockResolvedValue(undefined as any);
});
describe('module setup', () => {
@@ -158,7 +158,7 @@ describe('auto-icons module', () => {
});
it('should warn when base icon not found', async () => {
vi.mocked(fsExtra.pathExists).mockResolvedValue(false as any);
vi.mocked(fsPromises.access).mockRejectedValue(new Error('ENOENT'));
const options: AutoIconsOptions = {
enabled: true,
@@ -293,8 +293,9 @@ describe('auto-icons module', () => {
expect(mockSharpInstance.resize).toHaveBeenCalledWith(32);
expect(mockSharpInstance.resize).toHaveBeenCalledWith(16);
expect(fsExtra.ensureDir).toHaveBeenCalledWith(
expect(fsPromises.mkdir).toHaveBeenCalledWith(
resolve('/mock/dist', 'icons'),
{ recursive: true },
);
expect(output.publicAssets).toEqual([
@@ -514,7 +515,7 @@ describe('auto-icons module', () => {
};
// Make ensureDir throw an error
vi.mocked(fsExtra.ensureDir).mockRejectedValue(
vi.mocked(fsPromises.mkdir).mockRejectedValue(
new Error('Directory creation failed'),
);
@@ -530,7 +531,7 @@ describe('auto-icons module', () => {
if (buildHook) {
await buildHook(mockWxt as unknown as Wxt, output);
// But ensureDir should have been called
expect(fsExtra.ensureDir).toHaveBeenCalled();
expect(fsPromises.mkdir).toHaveBeenCalled();
}
});
+7 -3
View File
@@ -3,7 +3,7 @@ import { defineWxtModule } from 'wxt/modules';
import { resolve, relative } from 'node:path';
import defu from 'defu';
import sharp from 'sharp';
import { ensureDir, pathExists } from 'fs-extra';
import { access, mkdir } from 'node:fs/promises';
export default defineWxtModule<AutoIconsOptions>({
name: '@wxt-dev/auto-icons',
@@ -37,7 +37,11 @@ export default defineWxtModule<AutoIconsOptions>({
if (!parsedOptions.enabled)
return wxt.logger.warn(`\`[auto-icons]\` ${this.name} disabled`);
if (!(await pathExists(resolvedPath))) {
const iconExists = await access(resolvedPath).then(
() => true,
() => false,
);
if (!iconExists) {
return wxt.logger.warn(
`\`[auto-icons]\` Skipping icon generation, no base icon found at ${relative(process.cwd(), resolvedPath)}`,
);
@@ -91,7 +95,7 @@ export default defineWxtModule<AutoIconsOptions>({
}
}
ensureDir(resolve(outputFolder, 'icons'));
mkdir(resolve(outputFolder, 'icons'), { recursive: true });
await resizedImage.toFile(resolve(outputFolder, `icons/${size}.png`));
output.publicAssets.push({
+3 -3
View File
@@ -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.1.37",
"version": "0.1.38",
"type": "module",
"main": "src/index.mjs",
"types": "src/index.d.ts",
@@ -23,8 +23,8 @@
"src"
],
"devDependencies": {
"@types/chrome": "0.1.37",
"fs-extra": "^11.3.4",
"@types/chrome": "0.1.38",
"@types/node": "^20.0.0",
"nano-spawn": "^2.0.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
+8 -8
View File
@@ -1,5 +1,5 @@
import spawn from 'nano-spawn';
import fs from 'fs-extra';
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve, sep } from 'node:path';
import { sep as posixSep } from 'node:path/posix';
@@ -17,8 +17,8 @@ const pkgJsonPath = fileURLToPath(
import.meta.resolve('@types/chrome/package.json'),
);
const pkgDir = dirname(pkgJsonPath);
const pkgJson = await fs.readJson(pkgJsonPath);
const pkgJsonTemplate = await fs.readFile('templates/package.json', 'utf8');
const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
const pkgJsonTemplate = await readFile('templates/package.json', 'utf8');
const newPkgJson = JSON.parse(
pkgJsonTemplate.replaceAll('{{chromeTypesVersion}}', pkgJson.version),
);
@@ -27,7 +27,7 @@ newPkgJson.peerDependencies = pkgJson.peerDependencies;
newPkgJson.peerDependenciesMeta = pkgJson.peerDependenciesMeta;
const outPkgJsonPath = resolve('package.json');
await fs.writeJson(outPkgJsonPath, newPkgJson);
await writeFile(outPkgJsonPath, JSON.stringify(newPkgJson, null, 2));
await spawn('pnpm', ['-w', 'prettier', '--write', outPkgJsonPath]);
// Generate declaration files
@@ -35,7 +35,7 @@ await spawn('pnpm', ['-w', 'prettier', '--write', outPkgJsonPath]);
console.log('Generating declaration files');
const outDir = resolve('src/gen');
const declarationFileMapping = (
await fs.readdir(pkgDir, {
await readdir(pkgDir, {
recursive: true,
encoding: 'utf8',
})
@@ -50,11 +50,11 @@ const declarationFileMapping = (
}));
for (const { file, srcPath, destPath } of declarationFileMapping) {
const content = await fs.readFile(srcPath, 'utf8');
const content = await readFile(srcPath, 'utf8');
const transformedContent = transformFile(file, content);
const destDir = dirname(destPath);
await fs.mkdir(destDir, { recursive: true });
await fs.writeFile(destPath, transformedContent);
await mkdir(destDir, { recursive: true });
await writeFile(destPath, transformedContent);
console.log(` \x1b[2m-\x1b[0m \x1b[36m${file}\x1b[0m`);
}
+11 -8
View File
@@ -4076,14 +4076,17 @@ export namespace Browser {
id: string;
/**
* Implements the WebCrypto's SubtleCrypto interface. The cryptographic operations, including key generation, are hardware-backed.
* Only non-extractable keys can be generated. The supported key types are RSASSA-PKCS1-V1_5 and RSA-OAEP (on Chrome versions 134+) with `modulusLength` up to 2048 and ECDSA with `namedCurve` P-256. Each RSASSA-PKCS1-V1_5 and ECDSA key can be used for signing data at most once, unless the extension is allowlisted through the KeyPermissions policy, in which case the key can be used indefinitely. RSA-OAEP keys are supported since Chrome version 134 and can be used by extensions allowlisted through that same policy to unwrap other keys.
*
* Only non-extractable keys can be generated. The supported key types are RSASSA-PKCS1-V1_5 with `modulusLength` up to 2048 and ECDSA with `namedCurve` P-256. Each key can be used for signing data at most once, unless the extension is allowlisted by the KeyPermissions policy, in which case the key can be used indefinitely.
*
* Keys generated on a specific `Token` cannot be used with any other Tokens, nor can they be used with `window.crypto.subtle`. Equally, `Key` objects created with `window.crypto.subtle` cannot be used with this interface.
*/
subtleCrypto: SubtleCrypto;
/**
* Implements the WebCrypto's SubtleCrypto interface. The cryptographic operations, including key generation, are software-backed.
* Protection of the keys, and thus implementation of the non-extractable property, is done in software, so the keys are less protected than hardware-backed keys.
* Only non-extractable keys can be generated. The supported key types are RSASSA-PKCS1-V1_5 and RSA-OAEP (on Chrome versions 134+) with `modulusLength` up to 2048. Each RSASSA-PKCS1-V1_5 key can be used for signing data at most once, unless the extension is allowlisted through the KeyPermissions policy, in which case the key can be used indefinitely. RSA-OAEP keys are supported since Chrome version 134 and can be used by extensions allowlisted through that same policy to unwrap other keys.
* Implements the WebCrypto's SubtleCrypto interface. The cryptographic operations, including key generation, are software-backed. Protection of the keys, and thus implementation of the non-extractable property, is done in software, so the keys are less protected than hardware-backed keys.
*
* Only non-extractable keys can be generated. The only supported key type is RSASSA-PKCS1-V1_5 with `modulusLength` up to 2048. up to 2048. Each key can be used for signing data at most once, unless the extension is allowlisted through the KeyPermissions policy, in which case the key can be used indefinitely.
*
* Keys generated on a specific `Token` cannot be used with any other Tokens, nor can they be used with `window.crypto.subtle`. Equally, `Key` objects created with `window.crypto.subtle` cannot be used with this interface.
* @since Chrome 97
*/
@@ -9692,7 +9695,7 @@ export namespace Browser {
/** Sent after onSuspend to indicate that the app won't be unloaded after all. */
export const onSuspendCanceled: events.Event<() => void>;
/** Fired when a message is sent from either an extension process (by {@link runtime.sendMessage}) or a content script (by {@link tabs.sendMessage}). */
/** Fired when a message is sent from either {@link runtime.sendMessage} or {@link tabs.sendMessage}. */
export const onMessage: events.Event<
(message: any, sender: MessageSender, sendResponse: (response?: any) => void) => void
>;
@@ -11058,7 +11061,7 @@ export namespace Browser {
sessionId?: string | undefined;
/**
* The ID of the Split View that the tab belongs to.
* @since Chrome 145
* @since Chrome 140
*/
splitViewId?: number | undefined;
/**
@@ -11134,7 +11137,7 @@ export namespace Browser {
/**
* An ID that represents the absence of a split tab.
* @since Chrome 145
* @since Chrome 140
*/
export const SPLIT_VIEW_ID_NONE: -1;
@@ -11581,7 +11584,7 @@ export namespace Browser {
export function duplicate(tabId: number, callback: (tab?: Tab) => void): void;
/**
* Sends a single message to the content script(s) in the specified tab, with an optional callback to run when a response is sent back. The {@link runtime.onMessage} event is fired in each content script running in the specified tab for the current extension.
* Sends a single message to the content script(s) in the specified tab. The {@link runtime.onMessage} event is fired in each content script running in the specified tab for the current extension.
*
* Can return its result via Promise in Manifest V3 or later since Chrome 99.
*/
+1 -1
View File
@@ -24,7 +24,7 @@
],
"devDependencies": {
"@types/chrome": "{{chromeTypesVersion}}",
"fs-extra": "^11.3.4",
"@types/node": "^20.0.0",
"nano-spawn": "^2.0.0",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
+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 = [
+12
View File
@@ -1,5 +1,17 @@
# Changelog
## v1.2.2
[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.2.1...module-react-v1.2.2)
### 🩹 Fixes
- Add `@vitejs/plugin-react` v6 support ([c61fa8f8](https://github.com/wxt-dev/wxt/commit/c61fa8f8))
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
## v1.2.1
[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.2.0...module-react-v1.2.1)
+2 -2
View File
@@ -18,7 +18,7 @@
},
"license": "MIT",
"funding": "https://github.com/sponsors/wxt-dev",
"version": "1.2.1",
"version": "1.2.2",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -50,7 +50,7 @@
"wxt": ">=0.19.16"
},
"dependencies": {
"@vitejs/plugin-react": "^4.4.1 || ^5.0.0"
"@vitejs/plugin-react": "^4.4.1 || ^5.0.0 || ^6.0.0"
},
"devDependencies": {
"@types/react": "^19.2.14",
+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",
+60
View File
@@ -1,5 +1,65 @@
# Changelog
## v0.20.20
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.19...wxt-v0.20.20)
### 🩹 Fixes
- Unlisted script return values broken with Vite 8 sourcemaps ([#2197](https://github.com/wxt-dev/wxt/pull/2197))
### 📖 Documentation
- Add Dymo extension ID to UsingWxtSection.vue ([#2187](https://github.com/wxt-dev/wxt/pull/2187))
- Add Extension Rank Checker to the list of extensions ([#2193](https://github.com/wxt-dev/wxt/pull/2193))
### 🏡 Chore
- **deps:** Add vite-node 6 support ([64b68671](https://github.com/wxt-dev/wxt/commit/64b68671))
### ❤️ Contributors
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Joseph Hu ([@KiJO94GO](https://github.com/KiJO94GO))
- FJRG2007 ツ ([@FJRG2007](https://github.com/FJRG2007))
- Hampus Tågerud ([@hampustagerud](https://github.com/hampustagerud))
## v0.20.19
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.18...wxt-v0.20.19)
### 🚀 Enhancements
- Vite 8 support ([bfd4af5d](https://github.com/wxt-dev/wxt/commit/bfd4af5d))
### 🔥 Performance
- Use filter to improve plugin performance with rolldown ([#1787](https://github.com/wxt-dev/wxt/pull/1787))
### 🩹 Fixes
- List eslint 10 as supported ([55dc263d](https://github.com/wxt-dev/wxt/commit/55dc263d))
### 📖 Documentation
- Added "Scrape Similar" to the homepage ([#2158](https://github.com/wxt-dev/wxt/pull/2158))
- Added "isTrust" to the homepage ([#2161](https://github.com/wxt-dev/wxt/pull/2161))
### 🏡 Chore
- Add `prettier-plugin-jsdoc` to project ([#2171](https://github.com/wxt-dev/wxt/pull/2171))
- Replace fast-glob and ora with lighter alternatives ([#2173](https://github.com/wxt-dev/wxt/pull/2173))
- **deps:** Upgrade deps ([#2175](https://github.com/wxt-dev/wxt/pull/2175))
- Replace `fs-extra` with `node:fs/promises` ([#2174](https://github.com/wxt-dev/wxt/pull/2174))
### ❤️ Contributors
- Florian Metz ([@Timeraa](https://github.com/Timeraa))
- Aaron ([@aklinker1](https://github.com/aklinker1))
- Patryk Kuniczak ([@PatrykKuniczak](https://github.com/PatrykKuniczak))
- Etoome ([@etoome](https://github.com/etoome))
- Kuba ([@zizzfizzix](https://github.com/zizzfizzix))
## v0.20.18
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.20.17...wxt-v0.20.18)
+4 -4
View File
@@ -1,8 +1,8 @@
import { describe, it, expect } from 'vitest';
import { TestProject, WXT_PACKAGE_DIR } from '../utils';
import spawn from 'nano-spawn';
import { mkdir, writeFile } from 'node:fs/promises';
import { glob } from 'tinyglobby';
import { mkdir, writeJson } from 'fs-extra';
import { describe, expect, it } from 'vitest';
import { TestProject, WXT_PACKAGE_DIR } from '../utils';
describe('Init command', () => {
it('should download and create a template', async () => {
@@ -53,7 +53,7 @@ describe('Init command', () => {
it('should throw an error if the directory is not empty', async () => {
const project = new TestProject();
await mkdir(project.root, { recursive: true });
await writeJson(project.resolvePath('package.json'), {});
await writeFile(project.resolvePath('package.json'), JSON.stringify({}));
await expect(() =>
spawn(
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, vi } from 'vitest';
import { TestProject } from '../utils';
import type { InlineConfig, UnlistedScriptEntrypoint } from '../../src';
import { readFile } from 'fs-extra';
import { readFile } from 'node:fs/promises';
import { normalizePath } from '../../src';
describe('Module Helpers', () => {
+1 -1
View File
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
import { TestProject } from '../utils';
import extract from 'extract-zip';
import spawn from 'nano-spawn';
import { readFile, writeFile } from 'fs-extra';
import { readFile, writeFile } from 'node:fs/promises';
process.env.WXT_PNPM_IGNORE_WORKSPACE = 'true';
+10 -7
View File
@@ -1,8 +1,8 @@
import { glob } from 'tinyglobby';
import fs, { mkdir } from 'fs-extra';
import merge from 'lodash.merge';
import spawn from 'nano-spawn';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { dirname, relative, resolve } from 'path';
import { glob } from 'tinyglobby';
import {
InlineConfig,
UserConfig,
@@ -12,6 +12,7 @@ import {
zip,
} from '../src';
import { normalizePath } from '../src/core/utils';
import { pathExists, readJson } from '../src/core/utils/fs';
// Run "pnpm wxt" to use the "wxt" dev script, not the "wxt" binary from the
// wxt package. This uses the TS files instead of the compiled JS package
@@ -119,8 +120,8 @@ export class TestProject {
const [name, content] = file;
const filePath = this.resolvePath(name);
const fileDir = dirname(filePath);
await fs.ensureDir(fileDir);
await fs.writeFile(filePath, content ?? '', 'utf-8');
await mkdir(fileDir, { recursive: true });
await writeFile(filePath, content ?? '', 'utf-8');
}
// Only install dependencies if the project has custom ones - otherwise the
@@ -183,17 +184,19 @@ export class TestProject {
const absolutePath = this.resolvePath(path);
return [
normalizePath(relative(this.root, absolutePath)),
ignoreContents ? '<contents-ignored>' : await fs.readFile(absolutePath),
ignoreContents
? '<contents-ignored>'
: await readFile(absolutePath, 'utf-8'),
].join(`\n${''.padEnd(40, '-')}\n`);
}
pathExists(...path: string[]): Promise<boolean> {
return fs.pathExists(this.resolvePath(...path));
return pathExists(this.resolvePath(...path));
}
getOutputManifest(
path: string = '.output/chrome-mv3/manifest.json',
): Promise<any> {
return fs.readJson(this.resolvePath(path));
return readJson(this.resolvePath(path));
}
}
+4 -6
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.20.18",
"version": "0.20.20",
"description": "⚡ Next-gen Web Extension Framework",
"license": "MIT",
"scripts": {
@@ -30,11 +30,9 @@
"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",
"fs-extra": "^11.3.4",
"get-port-please": "^3.2.0",
"giget": "^1.2.3 || ^2.0.0 || ^3.0.0",
"hookable": "^6.0.1",
@@ -44,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",
@@ -53,13 +50,14 @@
"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",
"tinyglobby": "^0.2.15",
"unimport": "^3.13.1 || ^4.0.0 || ^5.0.0 || ^6.0.0",
"vite": "^5.4.19 || ^6.3.4 || ^7.0.0 || ^8.0.0-0",
"vite-node": "^3.2.4 || ^5.0.0",
"vite-node": "^3.2.4 || ^5.0.0 || ^6.0.0",
"web-ext-run": "^0.2.4"
},
"peerDependencies": {
@@ -72,10 +70,10 @@
},
"devDependencies": {
"@faker-js/faker": "^10.3.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/picomatch": "^4.0.2",
"@types/prompts": "^2.4.9",
"eslint": "^10.0.0",
"extract-zip": "^2.0.1",
+6 -6
View File
@@ -26,7 +26,7 @@ import { ViteNodeRunner } from 'vite-node/client';
import { installSourcemapsSupport } from 'vite-node/source-map';
import { createExtensionEnvironment } from '../../utils/environments';
import { dirname, extname, join, relative } from 'node:path';
import fs from 'fs-extra';
import { mkdir, readdir, rename, rmdir, stat } from 'node:fs/promises';
import { normalizePath } from '../../utils';
export async function createViteBuilder(
@@ -455,8 +455,8 @@ async function moveHtmlFiles(
);
const oldAbsPath = join(config.outDir, oldBundlePath);
const newAbsPath = join(config.outDir, newBundlePath);
await fs.ensureDir(dirname(newAbsPath));
await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
await mkdir(dirname(newAbsPath), { recursive: true });
await rename(oldAbsPath, newAbsPath);
return {
...chunk,
@@ -473,17 +473,17 @@ async function moveHtmlFiles(
/** Recursively remove all directories that are empty/ */
export async function removeEmptyDirs(dir: string): Promise<void> {
const files = await fs.readdir(dir);
const files = await readdir(dir);
for (const file of files) {
const filePath = join(dir, file);
const stats = await fs.stat(filePath);
const stats = await stat(filePath);
if (stats.isDirectory()) {
await removeEmptyDirs(filePath);
}
}
try {
await fs.rmdir(dir);
await rmdir(dir);
} catch {
// noop on failure - this means the directory was not empty.
}
@@ -0,0 +1,119 @@
import { describe, expect, it } from 'vitest';
import { iifeFooter } from '../iifeFooter';
interface OutputChunk {
type: 'chunk';
code: string;
isEntry: boolean;
}
interface OutputAsset {
type: 'asset';
source: string;
}
type OutputBundle = Record<string, OutputChunk | OutputAsset>;
function dedent(code: string) {
const lines = code.trim().split('\n');
return lines.map((line) => line.trimStart()).join('\n');
}
function createBundle(code: string): OutputBundle {
return {
'entry.js': {
type: 'chunk',
code: dedent(code),
isEntry: true,
},
};
}
function getCode(bundle: OutputBundle): string {
const entry = bundle['entry.js'];
if (entry.type !== 'chunk') {
throw new Error('expected chunk');
}
return entry.code;
}
function runPlugin(name: string, bundle: OutputBundle) {
const plugin = iifeFooter(name);
// @ts-expect-error -- calling the hook directly
plugin.generateBundle(undefined, bundle);
}
describe('IIFE return value plugin', () => {
it('should append return value when no sourcemap comment', () => {
const bundle = createBundle(`
var foo = (function(){return 1})();
`);
runPlugin('foo', bundle);
expect(getCode(bundle)).toBe(
dedent(`
var foo = (function(){return 1})();
foo;
`),
);
});
it('should insert return value before sourcemap comment', () => {
const bundle = createBundle(`
var foo = (function(){return 1})();
//# ${'sourceMappingURL'}=foo.js.map
`);
runPlugin('foo', bundle);
expect(getCode(bundle)).toBe(
dedent(`
var foo = (function(){return 1})();
foo;
//# ${'sourceMappingURL'}=foo.js.map
`),
);
});
it('should insert return value before inline sourcemap', () => {
const bundle = createBundle(`
var foo = (function(){return 1})();
//# ${'sourceMappingURL'}=data:application/json;base64,abc123
`);
runPlugin('foo', bundle);
expect(getCode(bundle)).toBe(
dedent(`
var foo = (function(){return 1})();
foo;
//# ${'sourceMappingURL'}=data:application/json;base64,abc123
`),
);
});
it('should skip non-entry chunks', () => {
const bundle = createBundle('var x = 1;');
(bundle['entry.js'] as OutputChunk).isEntry = false;
runPlugin('x', bundle);
expect(getCode(bundle)).toBe('var x = 1;');
});
it('should skip assets', () => {
const bundle: OutputBundle = {
'style.css': {
type: 'asset',
source: 'body {}',
} satisfies OutputAsset,
};
runPlugin('style', bundle);
expect((bundle['style.css'] as OutputAsset).source).toBe('body {}');
});
});
@@ -13,7 +13,20 @@ export function iifeFooter(iifeReturnValueName: string): Plugin {
generateBundle(_, bundle) {
for (const chunk of Object.values(bundle)) {
if (chunk.type === 'chunk' && chunk.isEntry) {
chunk.code += `${iifeReturnValueName};`;
const code = chunk.code;
const marker = '\n//# sourceMappingURL=';
const returnValue = `${iifeReturnValueName};`;
const index = code.indexOf(marker);
if (index >= 0) {
chunk.code =
code.slice(0, index + 1) +
`${returnValue}\n` +
code.slice(index + 1);
} else {
chunk.code += `\n${returnValue}`;
}
}
}
},
@@ -1,4 +1,4 @@
import { pathExists } from 'fs-extra';
import { pathExists } from '../../../utils/fs';
import { resolve } from 'node:path';
import type * as vite from 'vite';
import { ResolvedConfig } from '../../../../types';
@@ -1,3 +1,5 @@
import { readFile } from 'node:fs/promises';
import { resolve } from 'path';
import type { Plugin } from 'vite';
import { ResolvedConfig } from '../../../../types';
import { normalizePath } from '../../../utils';
@@ -5,8 +7,6 @@ import {
VirtualModuleId,
virtualModuleNames,
} from '../../../utils/virtual-modules';
import { resolve } from 'path';
import fs from 'fs-extra';
/**
* Resolve all the virtual modules to the `node_modules/wxt/dist/virtual`
@@ -36,7 +36,7 @@ export function resolveVirtualModules(config: ResolvedConfig): Plugin[] {
},
async handler(id) {
const inputPath = id.replace(resolvedVirtualId, '');
const template = await fs.readFile(
const template = await readFile(
resolve(config.wxtModuleDir, `dist/virtual/${name}.mjs`),
'utf-8',
);
+3 -3
View File
@@ -1,7 +1,7 @@
import { rm } from 'node:fs/promises';
import path from 'node:path';
import { glob } from 'tinyglobby';
import fs from 'fs-extra';
import pc from 'picocolors';
import { glob } from 'tinyglobby';
import { InlineConfig } from '../types';
import { registerWxt, wxt } from './wxt';
@@ -60,7 +60,7 @@ export async function clean(config?: string | InlineConfig) {
directories.map((dir) => pc.cyan(path.relative(root, dir))).join(', '),
);
for (const directory of directories) {
await fs.rm(directory, { force: true, recursive: true });
await rm(directory, { force: true, recursive: true });
wxt.logger.debug('Deleted ' + pc.cyan(path.relative(root, directory)));
}
}
+6 -6
View File
@@ -1,12 +1,12 @@
import { Entrypoint, WxtDirEntry, WxtDirFileEntry } from '../types';
import fs from 'fs-extra';
import { mkdir, readFile } from 'node:fs/promises';
import { dirname, relative, resolve } from 'node:path';
import { getEntrypointBundlePath, isHtmlEntrypoint } from './utils/entrypoints';
import { getEntrypointGlobals, getGlobals } from './utils/globals';
import { normalizePath } from './utils';
import path from 'node:path';
import { Message, parseI18nMessages } from './utils/i18n';
import { writeFileIfDifferent, getPublicFiles } from './utils/fs';
import { pathExists, writeFileIfDifferent, getPublicFiles } from './utils/fs';
import { wxt } from './wxt';
/**
@@ -14,7 +14,7 @@ import { wxt } from './wxt';
* directory.
*/
export async function generateWxtDir(entrypoints: Entrypoint[]): Promise<void> {
await fs.ensureDir(wxt.config.typesDir);
await mkdir(wxt.config.typesDir, { recursive: true });
const entries: WxtDirEntry[] = [
// Hard-coded entries
@@ -57,7 +57,7 @@ export async function generateWxtDir(entrypoints: Entrypoint[]): Promise<void> {
await Promise.all(
absoluteFileEntries.map(async (file) => {
await fs.ensureDir(dirname(file.path));
await mkdir(dirname(file.path), { recursive: true });
await writeFileIfDifferent(file.path, file.text);
}),
);
@@ -149,8 +149,8 @@ declare module "wxt/browser" {
'messages.json',
);
let messages: Message[];
if (await fs.pathExists(defaultLocalePath)) {
const content = JSON.parse(await fs.readFile(defaultLocalePath, 'utf-8'));
if (await pathExists(defaultLocalePath)) {
const content = JSON.parse(await readFile(defaultLocalePath, 'utf-8'));
messages = parseI18nMessages(content);
} else {
messages = parseI18nMessages({});
+10 -11
View File
@@ -1,7 +1,8 @@
import prompts from 'prompts';
import { consola } from 'consola';
import { downloadTemplate } from 'giget';
import fs from 'fs-extra';
import { readdir, rename } from 'node:fs/promises';
import { pathExists } from './utils/fs';
import path from 'node:path';
import pc from 'picocolors';
import { Formatter } from 'picocolors/types';
@@ -56,10 +57,10 @@ export async function initialize(options: {
input.template ??= defaultTemplate;
input.packageManager ??= options.packageManager;
const isExists = await fs.pathExists(input.directory);
const isExists = await pathExists(input.directory);
if (isExists) {
const isEmpty =
(await fs.readdir(input.directory)).filter((dir) => dir !== '.git')
(await readdir(input.directory)).filter((dir) => dir !== '.git')
.length === 0;
if (!isEmpty) {
consola.error(
@@ -167,14 +168,12 @@ async function cloneProject({
});
// 2. Move _gitignore -> .gitignore
await fs
.move(
path.join(directory, '_gitignore'),
path.join(directory, '.gitignore'),
)
.catch((err) =>
consola.warn('Failed to move _gitignore to .gitignore:', err),
);
await rename(
path.join(directory, '_gitignore'),
path.join(directory, '.gitignore'),
).catch((err) =>
consola.warn('Failed to move _gitignore to .gitignore:', err),
);
spinner.success();
} catch (err) {
@@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest';
import path from 'node:path';
import { npm } from '../npm';
import spawn from 'nano-spawn';
import { pathExists } from 'fs-extra';
import { pathExists } from '../../utils/fs';
describe('NPM Package Management Utils', () => {
describe('listDependencies', () => {
@@ -1,13 +1,13 @@
import { Dependency } from '../../types';
import { WxtPackageManagerImpl } from './types';
import path from 'node:path';
import { ensureDir } from 'fs-extra';
import { mkdir } from 'node:fs/promises';
import spawn from 'nano-spawn';
export const npm: WxtPackageManagerImpl = {
overridesKey: 'overrides',
async downloadDependency(id, downloadDir) {
await ensureDir(downloadDir);
await mkdir(downloadDir, { recursive: true });
const res = await spawn('npm', ['pack', id, '--json'], {
cwd: downloadDir,
});
+2 -2
View File
@@ -20,7 +20,7 @@ import { createFsCache } from './utils/cache';
import consola, { LogLevels } from 'consola';
import defu from 'defu';
import { NullablyRequired } from './utils/types';
import fs from 'fs-extra';
import { pathExists } from './utils/fs';
import { normalizePath } from './utils';
import { glob } from 'tinyglobby';
import { builtinModules } from '../builtin-modules';
@@ -543,7 +543,7 @@ function resolveWxtModuleDir() {
}
async function isDirMissing(dir: string) {
return !(await fs.pathExists(dir));
return !(await pathExists(dir));
}
function logMissingDir(logger: Logger, name: string, expected: string) {
@@ -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);
});
});
@@ -1,4 +1,7 @@
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { readFile } from 'node:fs/promises';
import { resolve } from 'path';
import { glob } from 'tinyglobby';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import {
BackgroundEntrypoint,
BackgroundEntrypointOptions,
@@ -9,21 +12,16 @@ import {
PopupEntrypoint,
SidepanelEntrypoint,
} from '../../../../types';
import { resolve } from 'path';
import { findEntrypoints } from '../find-entrypoints';
import fs from 'fs-extra';
import { glob } from 'tinyglobby';
import { fakeResolvedConfig, setFakeWxt } from '../../testing/fake-objects';
import { unnormalizePath } from '../../paths';
import { wxt } from '../../../wxt';
import { unnormalizePath } from '../../paths';
import { fakeResolvedConfig, setFakeWxt } from '../../testing/fake-objects';
import { findEntrypoints } from '../find-entrypoints';
vi.mock('tinyglobby');
const globMock = vi.mocked(glob);
vi.mock('fs-extra');
const readFileMock = vi.mocked(
fs.readFile as (path: string) => Promise<string>,
);
vi.mock('node:fs/promises');
const readFileMock = vi.mocked(readFile);
describe('findEntrypoints', () => {
const config = fakeResolvedConfig({
@@ -5,7 +5,7 @@ import {
ResolvedPublicFile,
} from '../../../types';
import { getPublicFiles } from '../fs';
import fs from 'fs-extra';
import { copyFile, mkdir, writeFile } from 'node:fs/promises';
import { dirname, resolve } from 'path';
import type { Spinner } from 'nanospinner';
import pc from 'picocolors';
@@ -50,11 +50,11 @@ async function copyPublicDirectory(): Promise<BuildOutput['publicAssets']> {
for (const file of files) {
const absoluteDest = resolve(wxt.config.outDir, file.relativeDest);
await fs.ensureDir(dirname(absoluteDest));
await mkdir(dirname(absoluteDest), { recursive: true });
if ('absoluteSrc' in file) {
await fs.copyFile(file.absoluteSrc, absoluteDest);
await copyFile(file.absoluteSrc, absoluteDest);
} else {
await fs.writeFile(absoluteDest, file.contents, 'utf8');
await writeFile(absoluteDest, file.contents, 'utf8');
}
publicAssets.push({
type: 'asset',
@@ -12,8 +12,8 @@ import {
IsolatedWorldContentScriptEntrypointOptions,
UnlistedScriptEntrypoint,
} from '../../../types';
import fs from 'fs-extra';
import { minimatch } from 'minimatch';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import picomatch from 'picomatch';
import { parseHTML } from 'linkedom';
import JSON5 from 'json5';
import { glob } from 'tinyglobby';
@@ -35,11 +35,11 @@ import { camelCase } from 'scule';
*/
export async function findEntrypoints(): Promise<Entrypoint[]> {
// Make sure required TSConfig file exists to load dependencies
await fs.mkdir(wxt.config.wxtDir, { recursive: true });
await mkdir(wxt.config.wxtDir, { recursive: true });
try {
await fs.writeJson(
await writeFile(
resolve(wxt.config.wxtDir, 'tsconfig.json'),
{},
JSON.stringify({}),
{ flag: 'wx' },
);
} catch (err) {
@@ -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];
@@ -204,7 +204,7 @@ async function importEntrypoints(infos: EntrypointInfo[]) {
async function importHtmlEntrypoint(
info: EntrypointInfo,
): Promise<Record<string, any>> {
const content = await fs.readFile(info.inputPath, 'utf-8');
const content = await readFile(info.inputPath, 'utf-8');
const { document } = parseHTML(content);
const metaTags = document.querySelectorAll('meta');
@@ -1,7 +1,7 @@
import { findEntrypoints } from './find-entrypoints';
import { BuildOutput, Entrypoint } from '../../../types';
import pc from 'picocolors';
import fs from 'fs-extra';
import { mkdir, rm } from 'node:fs/promises';
import { groupEntrypoints } from './group-entrypoints';
import { formatDuration } from '../time';
import { printBuildSummary } from '../log';
@@ -43,8 +43,8 @@ export async function internalBuild(): Promise<BuildOutput> {
const startTime = Date.now();
// Cleanup
await fs.rm(wxt.config.outDir, { recursive: true, force: true });
await fs.ensureDir(wxt.config.outDir);
await rm(wxt.config.outDir, { recursive: true, force: true });
await mkdir(wxt.config.outDir, { recursive: true });
const entrypoints = await findEntrypoints();
wxt.logger.debug('Detected entrypoints:', entrypoints);
@@ -111,7 +111,9 @@ async function combineAnalysisStats(): Promise<void> {
});
if (!wxt.config.analysis.keepArtifacts) {
await Promise.all(absolutePaths.map((statsFile) => fs.remove(statsFile)));
await Promise.all(
absolutePaths.map((statsFile) => rm(statsFile, { force: true })),
);
}
}
@@ -1,10 +1,10 @@
import { BuildOutput, Entrypoint, EntrypointGroup } from '../../../types';
import { generateWxtDir } from '../../generate-wxt-dir';
import { buildEntrypoints } from './build-entrypoints';
import { generateManifest, writeManifest } from '../../utils/manifest';
import { wxt } from '../../wxt';
import type { Browser } from '@wxt-dev/browser';
import { createSpinner } from 'nanospinner';
import { BuildOutput, Entrypoint, EntrypointGroup } from '../../../types';
import { generateWxtDir } from '../../generate-wxt-dir';
import { generateManifest, writeManifest } from '../../utils/manifest';
import { wxt } from '../../wxt';
import { buildEntrypoints } from './build-entrypoints';
/**
* Given a configuration, list of entrypoints, and an existing, partial output,
@@ -22,7 +22,7 @@ import { createSpinner } from 'nanospinner';
* .wxt directory.
* @param entrypointGroups The list of entrypoint groups to build.
* @param existingOutput The previous output to combine the rebuild results
* into. An emptry array if this is the first build.
* into. An empty array if this is the first build.
*/
export async function rebuild(
allEntrypoints: Entrypoint[],
+3 -3
View File
@@ -1,4 +1,4 @@
import fs, { ensureDir } from 'fs-extra';
import { mkdir, readFile } from 'node:fs/promises';
import { FsCache } from '../../types';
import { dirname, resolve } from 'path';
import { writeFileIfDifferent } from './fs';
@@ -16,13 +16,13 @@ export function createFsCache(wxtDir: string): FsCache {
return {
async set(key: string, value: string): Promise<void> {
const path = getPath(key);
await ensureDir(dirname(path));
await mkdir(dirname(path), { recursive: true });
await writeFileIfDifferent(path, value);
},
async get(key: string): Promise<string | undefined> {
const path = getPath(key);
try {
return await fs.readFile(path, 'utf-8');
return await readFile(path, 'utf-8');
} catch {
return undefined;
}
+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;
}
+18 -11
View File
@@ -1,7 +1,20 @@
import fs from 'fs-extra';
import { access, readFile, writeFile } from 'node:fs/promises';
import { glob } from 'tinyglobby';
import { unnormalizePath } from './paths';
import { wxt } from '../wxt';
import { unnormalizePath } from './paths';
export async function pathExists(path: string): Promise<boolean> {
try {
await access(path);
return true;
} catch {
return false;
}
}
export async function readJson<T = any>(path: string): Promise<T> {
return JSON.parse(await readFile(path, 'utf-8'));
}
/**
* Only write the contents to a file if it results in a change. This prevents
@@ -15,21 +28,15 @@ export async function writeFileIfDifferent(
file: string,
newContents: string,
): Promise<void> {
const existingContents = await fs
.readFile(file, 'utf-8')
.catch(() => undefined);
const existingContents = await readFile(file, 'utf-8').catch(() => undefined);
if (existingContents !== newContents) {
await fs.writeFile(file, newContents);
await writeFile(file, newContents);
}
}
/**
* Get all the files in the project's public directory. Returned paths are
* relative to the `config.publicDir`.
*/
export async function getPublicFiles(): Promise<string[]> {
if (!(await fs.pathExists(wxt.config.publicDir))) return [];
if (!(await pathExists(wxt.config.publicDir))) return [];
const files = await glob('**/*', {
cwd: wxt.config.publicDir,
@@ -1,6 +1,6 @@
import path from 'node:path';
import pc from 'picocolors';
import fs from 'fs-extra';
import { lstat } from 'node:fs/promises';
import { filesize } from 'filesize';
import { printTable } from './printTable';
@@ -20,7 +20,7 @@ export async function printFileList(
];
const prefix = i === files.length - 1 ? ' └─' : ' ├─';
const color = getChunkColor(file);
const stats = await fs.lstat(file);
const stats = await lstat(file);
totalSize += stats.size;
const size = String(filesize(stats.size));
return [
+2 -2
View File
@@ -7,7 +7,7 @@ import {
PopupEntrypoint,
SidepanelEntrypoint,
} from '../../types';
import fs from 'fs-extra';
import { mkdir } from 'node:fs/promises';
import { resolve } from 'path';
import { getEntrypointBundlePath } from './entrypoints';
import { ContentSecurityPolicy } from './content-security-policy';
@@ -33,7 +33,7 @@ export async function writeManifest(
? JSON.stringify(manifest)
: JSON.stringify(manifest, null, 2);
await fs.ensureDir(wxt.config.outDir);
await mkdir(wxt.config.outDir, { recursive: true });
await writeFileIfDifferent(resolve(wxt.config.outDir, 'manifest.json'), str);
output.publicAssets.unshift({
+2 -2
View File
@@ -1,5 +1,5 @@
import { resolve } from 'node:path';
import fs from 'fs-extra';
import { readJson } from './fs';
import { wxt } from '../wxt';
/**
@@ -12,7 +12,7 @@ export async function getPackageJson(): Promise<
> {
const file = resolve(wxt.config.root, 'package.json');
try {
return await fs.readJson(file);
return await readJson(file);
} catch (err) {
wxt.logger.debug(
`Failed to read package.json at: ${file}. Returning undefined.`,
@@ -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),
);
}
+9 -8
View File
@@ -1,6 +1,7 @@
import { InlineConfig } from '../types';
import path from 'node:path';
import fs from 'fs-extra';
import { mkdir, readFile } from 'node:fs/promises';
import { createWriteStream } from 'node:fs';
import { safeFilename } from './utils/strings';
import { getPackageJson } from './utils/package';
import { formatDuration } from './utils/time';
@@ -10,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.
@@ -43,7 +44,7 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
.replaceAll('{{mode}}', wxt.config.mode)
.replaceAll('{{manifestVersion}}', `mv${wxt.config.manifestVersion}`);
await fs.ensureDir(wxt.config.outBaseDir);
await mkdir(wxt.config.outBaseDir, { recursive: true });
// ZIP output directory
await wxt.hooks.callHook('zip:extension:start', wxt);
@@ -124,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 = [
@@ -137,13 +138,13 @@ async function zipDir(
for (const file of filesToZip) {
const absolutePath = path.resolve(directory, file);
if (file.endsWith('.json')) {
const content = await fs.readFile(absolutePath, 'utf-8');
const content = await readFile(absolutePath, 'utf-8');
archive.file(
file,
(await options?.transform?.(absolutePath, file, content)) || content,
);
} else {
const content = await fs.readFile(absolutePath);
const content = await readFile(absolutePath);
archive.file(file, content);
}
}
@@ -160,7 +161,7 @@ async function zipDir(
compressionOptions: { level: wxt.config.zip.compressionLevel },
}),
})
.pipe(fs.createWriteStream(outputPath))
.pipe(createWriteStream(outputPath))
.on('error', reject)
.on('close', resolve),
);
+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
+5 -2
View File
@@ -1,4 +1,4 @@
import { pathExists, rm } from 'fs-extra';
import { access, rm } from 'node:fs/promises';
let setupHappened = false;
@@ -12,7 +12,10 @@ export async function setup() {
globalThis.__ENTRYPOINT__ = 'test';
const e2eDistPath = './e2e/dist/';
if (await pathExists(e2eDistPath)) {
try {
await access(e2eDistPath);
await rm(e2eDistPath, { recursive: true, force: true });
} catch {
// Directory doesn't exist, nothing to clean up
}
}
+34 -95
View File
@@ -25,9 +25,6 @@ importers:
'@commitlint/types':
specifier: ^20.4.3
version: 20.4.3
'@types/fs-extra':
specifier: ^11.0.4
version: 11.0.4
'@types/semver':
specifier: ^7.7.1
version: 7.7.1
@@ -43,12 +40,6 @@ importers:
feed:
specifier: ^5.2.0
version: 5.2.0
fs-extra:
specifier: ^11.3.4
version: 11.3.4
lint-staged:
specifier: ^16.3.2
version: 16.3.2
markdown-it-footnote:
specifier: ^4.0.0
version: 4.0.0(patch_hash=5b74a0001a771ff5df790dd36b6883c4794e1f666b231499edb2d65eae8f4930)
@@ -58,6 +49,9 @@ importers:
nano-spawn:
specifier: ^2.0.0
version: 2.0.0
nano-staged:
specifier: ^0.8.0
version: 0.8.0
p-map:
specifier: ^7.0.4
version: 7.0.4
@@ -149,9 +143,6 @@ importers:
defu:
specifier: ^6.1.4
version: 6.1.4
fs-extra:
specifier: ^11.3.4
version: 11.3.4
sharp:
specifier: ^0.34.5
version: 0.34.5
@@ -182,11 +173,11 @@ importers:
version: 1.2.16
devDependencies:
'@types/chrome':
specifier: 0.1.37
version: 0.1.37
fs-extra:
specifier: ^11.3.4
version: 11.3.4
specifier: 0.1.38
version: 0.1.38
'@types/node':
specifier: ^20.0.0
version: 20.19.32
nano-spawn:
specifier: ^2.0.0
version: 2.0.0
@@ -256,7 +247,7 @@ importers:
packages/module-react:
dependencies:
'@vitejs/plugin-react':
specifier: ^4.4.1 || ^5.0.0
specifier: ^4.4.1 || ^5.0.0 || ^6.0.0
version: 5.1.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:
'@types/react':
@@ -306,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:
@@ -465,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
@@ -477,9 +465,6 @@ importers:
filesize:
specifier: ^11.0.13
version: 11.0.13
fs-extra:
specifier: ^11.3.4
version: 11.3.4
get-port-please:
specifier: ^3.2.0
version: 3.2.0
@@ -507,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
@@ -534,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
@@ -553,7 +538,7 @@ importers:
specifier: ^5.4.19 || ^6.3.4 || ^7.0.0 || ^8.0.0-0
version: 7.3.1(@types/node@20.19.32)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2)
vite-node:
specifier: ^3.2.4 || ^5.0.0
specifier: ^3.2.4 || ^5.0.0 || ^6.0.0
version: 5.3.0(@types/node@20.19.32)(jiti@2.6.1)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2)
web-ext-run:
specifier: ^0.2.4
@@ -562,9 +547,6 @@ importers:
'@faker-js/faker':
specifier: ^10.3.0
version: 10.3.0
'@types/fs-extra':
specifier: ^11.0.4
version: 11.0.4
'@types/lodash.merge':
specifier: ^4.6.9
version: 4.6.9
@@ -574,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
@@ -2144,8 +2129,8 @@ packages:
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
'@types/chrome@0.1.37':
resolution: {integrity: sha512-IJE4ceuDO7lrEuua7Pow47zwNcI8E6qqkowRP7aFPaZ0lrjxh6y836OPqqkIZeTX64FTogbw+4RNH0+QrweCTQ==}
'@types/chrome@0.1.38':
resolution: {integrity: sha512-5aK4m9wZqoWAoB98aElESLm/5pXpqJnFWMNoiCs/XdPsXR6wNdVkJFSdQ9Wr4PnTuUrxD0SuNuDHh3EG5QeBzA==}
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -2165,9 +2150,6 @@ packages:
'@types/filewriter@0.0.33':
resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
'@types/fs-extra@11.0.4':
resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
'@types/har-format@1.2.16':
resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
@@ -2180,9 +2162,6 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/jsonfile@6.1.4':
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
'@types/katex@0.16.8':
resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==}
@@ -2216,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==}
@@ -3630,19 +3612,10 @@ packages:
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
lint-staged@16.3.2:
resolution: {integrity: sha512-xKqhC2AeXLwiAHXguxBjuChoTTWFC6Pees0SHPwOpwlvI3BH7ZADFPddAdN3pgo3aiKgPUx/bxE78JfUnxQnlg==}
engines: {node: '>=20.17'}
hasBin: true
listr2@10.1.0:
resolution: {integrity: sha512-/6t2KgDYIcCjhELwvrRxi1gaJ4xCGLTjNvh6mSjYenBkrZxggek8EwCbwBU33GMUCpyyrOzz2TzylrO5mTiI1w==}
engines: {node: '>=22.0.0'}
listr2@9.0.5:
resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==}
engines: {node: '>=20.0.0'}
local-pkg@1.1.2:
resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
engines: {node: '>=14'}
@@ -3675,10 +3648,6 @@ packages:
lodash.once@4.1.1:
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
log-update@6.1.0:
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
engines: {node: '>=18'}
log-update@7.1.0:
resolution: {integrity: sha512-y9pi/ZOQQVvTgfRDEHV1Cj4zQUkJZPipEUNOxhn1R6KgmdMs7LKvXWCd9eMVPGJgvYzFLCenecWr0Ps8ChVv2A==}
engines: {node: '>=20'}
@@ -3924,6 +3893,11 @@ packages:
resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==}
engines: {node: '>=20.17'}
nano-staged@0.8.0:
resolution: {integrity: sha512-QSEqPGTCJbkHU2yLvfY6huqYPjdBrOaTMKatO1F8nCSrkQGXeKwtCiCnsdxnuMhbg3DTVywKaeWLGCE5oJpq0g==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
hasBin: true
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -4501,10 +4475,6 @@ packages:
std-env@3.10.0:
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -6270,7 +6240,7 @@ snapshots:
'@types/deep-eql': 4.0.2
assertion-error: 2.0.1
'@types/chrome@0.1.37':
'@types/chrome@0.1.38':
dependencies:
'@types/filesystem': 0.0.36
'@types/har-format': 1.2.16
@@ -6291,11 +6261,6 @@ snapshots:
'@types/filewriter@0.0.33': {}
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 20.19.32
'@types/har-format@1.2.16': {}
'@types/hast@3.0.4':
@@ -6306,10 +6271,6 @@ snapshots:
'@types/json-schema@7.0.15': {}
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 20.19.32
'@types/katex@0.16.8': {}
'@types/linkify-it@5.0.0': {}
@@ -6341,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
@@ -7803,15 +7766,6 @@ snapshots:
dependencies:
uc.micro: 2.1.0
lint-staged@16.3.2:
dependencies:
commander: 14.0.3
listr2: 9.0.5
micromatch: 4.0.8
string-argv: 0.3.2
tinyexec: 1.0.2
yaml: 2.8.2
listr2@10.1.0:
dependencies:
cli-truncate: 5.1.1
@@ -7821,15 +7775,6 @@ snapshots:
rfdc: 1.4.1
wrap-ansi: 9.0.2
listr2@9.0.5:
dependencies:
cli-truncate: 5.1.1
colorette: 2.0.20
eventemitter3: 5.0.4
log-update: 6.1.0
rfdc: 1.4.1
wrap-ansi: 9.0.2
local-pkg@1.1.2:
dependencies:
mlly: 1.8.1
@@ -7856,14 +7801,6 @@ snapshots:
lodash.once@4.1.1: {}
log-update@6.1.0:
dependencies:
ansi-escapes: 7.3.0
cli-cursor: 5.0.0
slice-ansi: 7.1.2
strip-ansi: 7.1.2
wrap-ansi: 9.0.2
log-update@7.1.0:
dependencies:
ansi-escapes: 7.3.0
@@ -8288,6 +8225,10 @@ snapshots:
nano-spawn@2.0.0: {}
nano-staged@0.8.0:
dependencies:
picocolors: 1.1.1
nanoid@3.3.11: {}
nanospinner@1.2.2:
@@ -8997,8 +8938,6 @@ snapshots:
std-env@3.10.0: {}
string-argv@0.3.2: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
+14 -10
View File
@@ -6,7 +6,7 @@ import {
parseCommits,
} from 'changelogen';
import { consola } from 'consola';
import fs from 'fs-extra';
import { readdir, readFile, writeFile } from 'node:fs/promises';
import spawn from 'nano-spawn';
import path from 'node:path';
import { getPkgTag, grabPackageDetails, listCommitsInDir } from './git';
@@ -41,7 +41,7 @@ if (currentVersion.startsWith('0.')) {
await spawn('pnpm', ['version', bumpType], {
cwd: pkgDir,
});
const updatedPkgJson = await fs.readJson(pkgJsonPath);
const updatedPkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
const newVersion: string = updatedPkgJson.version;
const newTag = getPkgTag(pkg, newVersion);
consola.info('Bump:', { currentVersion, bumpType, newVersion });
@@ -63,8 +63,7 @@ if (originalBumpType === 'major') {
`[⚠️ breaking changes](https://wxt.dev/guide/resources/upgrading.html) &bull; [compare changes]`,
);
}
const { releases: prevReleases } = await fs
.readFile(changelogPath, 'utf8')
const { releases: prevReleases } = await readFile(changelogPath, 'utf8')
.then(parseChangelogMarkdown)
.catch(() => ({ releases: [] }));
const allReleases = [
@@ -80,29 +79,34 @@ const newChangelog =
allReleases
.map((release) => [`## v${release.version}`, release.body].join('\n\n'))
.join('\n\n');
await fs.writeFile(changelogPath, newChangelog, 'utf8');
await writeFile(changelogPath, newChangelog, 'utf8');
consola.success('Updated changelog');
// Update WXT version in templates when releasing wxt package
const templatePkgJsonPaths: string[] = [];
if (pkg === 'wxt') {
const templatesDir = 'templates';
const templateDirs = await fs.readdir(templatesDir);
const templateDirs = await readdir(templatesDir);
for (const templateDir of templateDirs) {
const templatePkgJsonPath = path.join(
templatesDir,
templateDir,
'package.json',
);
if (await fs.pathExists(templatePkgJsonPath)) {
const templatePkgJson = await fs.readJson(templatePkgJsonPath);
try {
const templatePkgJson = JSON.parse(
await readFile(templatePkgJsonPath, 'utf-8'),
);
if (templatePkgJson.devDependencies?.wxt) {
templatePkgJson.devDependencies.wxt = `^${newVersion}`;
await fs.writeJson(templatePkgJsonPath, templatePkgJson, { spaces: 2 });
await writeFile(
templatePkgJsonPath,
JSON.stringify(templatePkgJson, null, 2),
);
templatePkgJsonPaths.push(templatePkgJsonPath);
consola.success(`Updated wxt version in ${templatePkgJsonPath}`);
}
}
} catch {}
}
}
+2 -3
View File
@@ -4,7 +4,7 @@ import {
loadChangelogConfig,
parseChangelogMarkdown,
} from 'changelogen';
import fs from 'fs-extra';
import { readFile } from 'node:fs/promises';
import { grabPackageDetails } from './git';
import consola from 'consola';
@@ -19,8 +19,7 @@ const { pkgName, prevTag, currentVersion, changelogPath } =
await grabPackageDetails(pkg);
consola.info('Creating release for:', { pkg, pkgName, prevTag });
const { releases } = await fs
.readFile(changelogPath, 'utf8')
const { releases } = await readFile(changelogPath, 'utf8')
.then(parseChangelogMarkdown)
.catch(() => ({ releases: [] }));
+2 -2
View File
@@ -1,11 +1,11 @@
import { RawGitCommit, getGitDiff } from 'changelogen';
import { consola } from 'consola';
import fs from 'fs-extra';
import { readFile } from 'node:fs/promises';
export async function grabPackageDetails(pkg: string) {
const pkgDir = `packages/${pkg}`;
const pkgJsonPath = `${pkgDir}/package.json`;
const pkgJson = await fs.readJson(pkgJsonPath);
const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
const currentVersion: string = pkgJson.version;
return {
pkgDir,
+2 -3
View File
@@ -5,7 +5,7 @@ import {
updateGithubRelease,
} from 'changelogen';
import { getPkgTag, grabPackageDetails } from './git';
import fs from 'fs-extra';
import { readFile } from 'node:fs/promises';
import consola from 'consola';
const pkg = process.argv[2];
@@ -17,8 +17,7 @@ if (!pkg) {
// Update
const { changelogPath, pkgName } = await grabPackageDetails(pkg);
const { releases } = await fs
.readFile(changelogPath, 'utf8')
const { releases } = await readFile(changelogPath, 'utf8')
.then(parseChangelogMarkdown)
.catch(() => ({ releases: [] }));
const config = await loadChangelogConfig(process.cwd());
+7 -7
View File
@@ -1,10 +1,10 @@
import { glob } from 'tinyglobby';
import fs from 'fs-extra';
import * as semver from 'semver';
import { dirname } from 'node:path';
import consola from 'consola';
import spawn from 'nano-spawn';
import { readFile, writeFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import pMap from 'p-map';
import * as semver from 'semver';
import { glob } from 'tinyglobby';
const HELP_MESSAGE = `
Upgrades dependencies throughout WXT using custom rules.
@@ -101,7 +101,7 @@ async function getPackageJsonDependencies(
);
const packageJsons: PackageJsonData[] = await Promise.all(
packageJsonFiles.map(async (path) => ({
content: await fs.readJson(path),
content: JSON.parse(await readFile(path, 'utf-8')),
path,
folder: dirname(path),
})),
@@ -341,7 +341,7 @@ async function writeUpgrades(
upgrades: UpgradeDetails[],
) {
for (const packageJsonFile of packageJsonFiles) {
const oldText = await fs.readFile(packageJsonFile, 'utf8');
const oldText = await readFile(packageJsonFile, 'utf8');
let newText = oldText;
for (const upgrade of upgrades) {
const search = `"${upgrade.name}": "${upgrade.currentRange}"`;
@@ -349,7 +349,7 @@ async function writeUpgrades(
newText = newText.replaceAll(search, replace);
}
if (newText !== oldText) {
await fs.writeFile(packageJsonFile, newText, 'utf8');
await writeFile(packageJsonFile, newText, 'utf8');
}
}
}
+1 -1
View File
@@ -23,6 +23,6 @@
"@types/react-dom": "^19.2.3",
"@wxt-dev/module-react": "^1.1.5",
"typescript": "^5.9.3",
"wxt": "^0.20.18"
"wxt": "^0.20.20"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"devDependencies": {
"@wxt-dev/module-solid": "^1.1.4",
"typescript": "^5.9.3",
"wxt": "^0.20.18"
"wxt": "^0.20.20"
}
}
+1 -1
View File
@@ -21,6 +21,6 @@
"svelte-check": "^4.4.4",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"wxt": "^0.20.18"
"wxt": "^0.20.20"
}
}
+1 -4
View File
@@ -1,6 +1,3 @@
{
"extends": "./.wxt/tsconfig.json",
"compilerOptions": {
"useDefineForClassFields": true
}
"extends": "./.wxt/tsconfig.json"
}
+1 -1
View File
@@ -16,6 +16,6 @@
},
"devDependencies": {
"typescript": "^5.9.3",
"wxt": "^0.20.18"
"wxt": "^0.20.20"
}
}
+1 -1
View File
@@ -21,6 +21,6 @@
"@wxt-dev/module-vue": "^1.0.3",
"typescript": "^5.9.3",
"vue-tsc": "^3.2.5",
"wxt": "^0.20.18"
"wxt": "^0.20.20"
}
}