wxt 0.20.7 → 0.20.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,51 +1,33 @@
1
1
  <!-- DO NOT EDIT, THIS FILE WAS GENERATED BY '../../scripts/generate-readmes.sh' -->
2
- <h1 align="center">
3
- <img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo">
4
- <span>WXT</span>
5
- </h1>
6
-
7
- <p align="center">
8
- <a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="npm version" src="https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048"></a>
9
- <span> </span>
10
- <a href="https://www.npmjs.com/package/wxt" target="_blank"><img alt="downloads" src="https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048"></a>
11
- <span> </span>
12
- <a href="https://github.com/wxt-dev/wxt/blob/main/LICENSE" target="_blank"><img alt="license | MIT" src="https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048"></a>
13
- <span> </span>
14
- <a href="https://codecov.io/github/wxt-dev/wxt" target="_blank"><img alt="coverage" src="https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048"></a>
15
- </p>
16
-
17
- <p align="center">
18
- <span>Next-gen framework for developing web extensions.</span>
19
- <br/>
20
- <span>⚡</span>
21
- <br/>
22
- <q><i>It's like Nuxt, but for Web Extensions</i></q>
23
- </p>
24
-
25
- <p align="center">
26
- <a href="https://wxt.dev/guide/installation.html" target="_blank">Get Started</a>
27
- &bull;
28
- <a href="https://wxt.dev/api/config.html" target="_blank">Configuration</a>
29
- &bull;
30
- <a href="https://wxt.dev/examples.html" target="_blank">Examples</a>
31
- &bull;
32
- <a href="https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md" target="_blank">Changelog</a>
33
- &bull;
34
- <a href="https://discord.gg/ZFsZqGery9" target="_blank">Discord</a>
35
- </p>
2
+ <div align="center">
3
+
4
+ # <img align="top" width="44" src="https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/public/hero-logo.svg" alt="WXT Logo"> WXT
5
+
6
+ [![npm version](https://img.shields.io/npm/v/wxt?labelColor=black&color=%234fa048)](https://www.npmjs.com/package/wxt)
7
+ [![downloads](https://img.shields.io/npm/dm/wxt?labelColor=black&color=%234fa048)](https://www.npmjs.com/package/wxt)
8
+ [![license | MIT](https://img.shields.io/npm/l/wxt?labelColor=black&color=%234fa048)](https://github.com/wxt-dev/wxt/blob/main/LICENSE)
9
+ [![coverage](https://img.shields.io/codecov/c/github/wxt-dev/wxt?labelColor=black&color=%234fa048)](https://codecov.io/github/wxt-dev/wxt)
10
+
11
+ Next-gen framework for developing web extensions.<br/>⚡<br/><q><i>It's like Nuxt, but for Web Extensions</i></q>
12
+
13
+ [Get Started](https://wxt.dev/guide/installation.html) •
14
+ [Configuration](https://wxt.dev/api/config.html)
15
+ [Examples](https://wxt.dev/examples.html) •
16
+ [Changelog](https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md) •
17
+ [Discord](https://discord.gg/ZFsZqGery9)
18
+
19
+ </div>
36
20
 
37
21
  ![Example CLI Output](https://raw.githubusercontent.com/wxt-dev/wxt/HEAD/docs/assets/cli-output.png)
38
22
 
39
23
  ## Demo
40
24
 
41
- https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94
25
+ <https://github.com/wxt-dev/wxt/assets/10101283/4d678939-1bdb-495c-9c36-3aa281d84c94>
42
26
 
43
27
  ## Quick Start
44
28
 
45
29
  Bootstrap a new project:
46
30
 
47
- <!-- automd:pm-x version="latest" name="wxt" args="init" -->
48
-
49
31
  ```sh
50
32
  # npm
51
33
  npx wxt@latest init
@@ -57,8 +39,6 @@ pnpm dlx wxt@latest init
57
39
  bunx wxt@latest init
58
40
  ```
59
41
 
60
- <!-- /automd -->
61
-
62
42
  Or see the [installation guide](https://wxt.dev/guide/installation.html) to get started with WXT.
63
43
 
64
44
  ## Features
@@ -80,17 +60,11 @@ Or see the [installation guide](https://wxt.dev/guide/installation.html) to get
80
60
 
81
61
  WXT is a [MIT-licensed](https://github.com/wxt-dev/wxt/blob/main/LICENSE) open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring WXT's development](https://github.com/sponsors/wxt-dev).
82
62
 
83
- <a href="https://github.com/sponsors/wxt-dev"><img alt="WXT Sponsors" src="https://raw.githubusercontent.com/wxt-dev/static/refs/heads/main/sponsorkit/sponsors.svg"></a>
63
+ [![WXT Sponsors](https://raw.githubusercontent.com/wxt-dev/static/refs/heads/main/sponsorkit/sponsors.svg)](https://github.com/sponsors/wxt-dev)
84
64
 
85
65
  ## Contributors
86
66
 
87
- <!-- automd:contributors author="aklinker1" license="MIT" github="wxt-dev/wxt" -->
88
-
89
67
  Published under the [MIT](https://github.com/wxt-dev/wxt/blob/main/LICENSE) license.
90
68
  Made by [@aklinker1](https://github.com/aklinker1) and [community](https://github.com/wxt-dev/wxt/graphs/contributors) 💛
91
- <br><br>
92
- <a href="https://github.com/wxt-dev/wxt/graphs/contributors">
93
- <img src="https://contrib.rocks/image?repo=wxt-dev/wxt" />
94
- </a>
95
69
 
96
- <!-- /automd -->
70
+ [![WXT contributors](https://contrib.rocks/image?repo=wxt-dev/wxt)](https://github.com/wxt-dev/wxt/graphs/contributors)
@@ -1,3 +1,7 @@
1
1
  import { ResolvedConfig, WxtBuilder, WxtDevServer, WxtHooks } from '../../../types';
2
2
  import { Hookable } from 'hookable';
3
3
  export declare function createViteBuilder(wxtConfig: ResolvedConfig, hooks: Hookable<WxtHooks>, getWxtDevServer?: () => WxtDevServer | undefined): Promise<WxtBuilder>;
4
+ /**
5
+ * Recursively remove all directories that are empty/
6
+ */
7
+ export declare function removeEmptyDirs(dir: string): Promise<void>;
@@ -9,7 +9,9 @@ import { ViteNodeServer } from "vite-node/server";
9
9
  import { ViteNodeRunner } from "vite-node/client";
10
10
  import { installSourcemapsSupport } from "vite-node/source-map";
11
11
  import { createExtensionEnvironment } from "../../utils/environments/index.mjs";
12
- import { relative } from "node:path";
12
+ import { dirname, extname, join, relative } from "node:path";
13
+ import fs from "fs-extra";
14
+ import { normalizePath } from "../../utils/paths.mjs";
13
15
  export async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
14
16
  const vite = await import("vite");
15
17
  const getBaseConfig = async (baseConfigOptions) => {
@@ -62,20 +64,16 @@ export async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
62
64
  const plugins = [
63
65
  wxtPlugins.entrypointGroupGlobals(entrypoint)
64
66
  ];
67
+ const iifeReturnValueName = safeVarName(entrypoint.name);
65
68
  if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
66
69
  plugins.push(wxtPlugins.cssEntrypoints(entrypoint, wxtConfig));
67
70
  }
68
- const iifeReturnValueName = safeVarName(entrypoint.name);
69
- const libMode = {
71
+ if (entrypoint.type === "content-script" || entrypoint.type === "unlisted-script") {
72
+ plugins.push(wxtPlugins.iifeFooter(iifeReturnValueName));
73
+ }
74
+ return {
70
75
  mode: wxtConfig.mode,
71
76
  plugins,
72
- esbuild: {
73
- // Add a footer with the returned value so it can return values to `scripting.executeScript`
74
- // Footer is added a part of esbuild to make sure it's not minified. It
75
- // get's removed if added to `build.rollupOptions.output.footer`
76
- // See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
77
- footer: iifeReturnValueName + ";"
78
- },
79
77
  build: {
80
78
  lib: {
81
79
  entry,
@@ -109,7 +107,6 @@ export async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
109
107
  "process.env.NODE_ENV": JSON.stringify(wxtConfig.mode)
110
108
  }
111
109
  };
112
- return libMode;
113
110
  };
114
111
  const getMultiPageConfig = (entrypoints) => {
115
112
  const htmlEntrypoints = new Set(
@@ -117,10 +114,7 @@ export async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
117
114
  );
118
115
  return {
119
116
  mode: wxtConfig.mode,
120
- plugins: [
121
- wxtPlugins.multipageMove(entrypoints, wxtConfig),
122
- wxtPlugins.entrypointGroupGlobals(entrypoints)
123
- ],
117
+ plugins: [wxtPlugins.entrypointGroupGlobals(entrypoints)],
124
118
  build: {
125
119
  rollupOptions: {
126
120
  input: entrypoints.reduce((input, entry) => {
@@ -238,7 +232,7 @@ export async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
238
232
  async build(group) {
239
233
  let entryConfig;
240
234
  if (Array.isArray(group)) entryConfig = getMultiPageConfig(group);
241
- else if (group.inputPath.endsWith(".css"))
235
+ else if (group.type === "content-script-style" || group.type === "unlisted-style")
242
236
  entryConfig = getCssConfig(group);
243
237
  else entryConfig = getLibModeConfig(group);
244
238
  const buildConfig = vite.mergeConfig(await getBaseConfig(), entryConfig);
@@ -248,9 +242,10 @@ export async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
248
242
  buildConfig
249
243
  );
250
244
  const result = await vite.build(buildConfig);
245
+ const chunks = getBuildOutputChunks(result);
251
246
  return {
252
247
  entrypoints: group,
253
- chunks: getBuildOutputChunks(result)
248
+ chunks: await moveHtmlFiles(wxtConfig, group, chunks)
254
249
  };
255
250
  },
256
251
  async createServer(info) {
@@ -315,3 +310,47 @@ function getRollupEntry(entrypoint) {
315
310
  }
316
311
  return entrypoint.inputPath;
317
312
  }
313
+ async function moveHtmlFiles(config, group, chunks) {
314
+ if (!Array.isArray(group)) return chunks;
315
+ const entryMap = group.reduce((map, entry) => {
316
+ const a = normalizePath(relative(config.root, entry.inputPath));
317
+ map[a] = entry;
318
+ return map;
319
+ }, {});
320
+ const movedChunks = await Promise.all(
321
+ chunks.map(async (chunk) => {
322
+ if (!chunk.fileName.endsWith(".html")) return chunk;
323
+ const entry = entryMap[chunk.fileName];
324
+ const oldBundlePath = chunk.fileName;
325
+ const newBundlePath = getEntrypointBundlePath(
326
+ entry,
327
+ config.outDir,
328
+ extname(chunk.fileName)
329
+ );
330
+ const oldAbsPath = join(config.outDir, oldBundlePath);
331
+ const newAbsPath = join(config.outDir, newBundlePath);
332
+ await fs.ensureDir(dirname(newAbsPath));
333
+ await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
334
+ return {
335
+ ...chunk,
336
+ fileName: newBundlePath
337
+ };
338
+ })
339
+ );
340
+ removeEmptyDirs(config.outDir);
341
+ return movedChunks;
342
+ }
343
+ export async function removeEmptyDirs(dir) {
344
+ const files = await fs.readdir(dir);
345
+ for (const file of files) {
346
+ const filePath = join(dir, file);
347
+ const stats = await fs.stat(filePath);
348
+ if (stats.isDirectory()) {
349
+ await removeEmptyDirs(filePath);
350
+ }
351
+ }
352
+ try {
353
+ await fs.rmdir(dir);
354
+ } catch {
355
+ }
356
+ }
@@ -6,7 +6,7 @@ export function devServerGlobals(config, server) {
6
6
  return {
7
7
  define: {
8
8
  __DEV_SERVER_ORIGIN__: JSON.stringify(
9
- server.origin.replace(/^http(s):/, "ws$1:")
9
+ server.origin.replace(/^http(s?):/, "ws$1:")
10
10
  )
11
11
  }
12
12
  };
@@ -0,0 +1,8 @@
1
+ import type { Plugin } from 'vite';
2
+ /**
3
+ * Add a footer with the returned value so it can return values to `scripting.executeScript`
4
+ * Footer is added a part of esbuild to make sure it's not minified. It
5
+ * get's removed if added to `build.rollupOptions.output.footer`
6
+ * See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
7
+ */
8
+ export declare function iifeFooter(iifeReturnValueName: string): Plugin;
@@ -0,0 +1,12 @@
1
+ export function iifeFooter(iifeReturnValueName) {
2
+ return {
3
+ name: "wxt:iife-footer",
4
+ generateBundle(_, bundle) {
5
+ for (const chunk of Object.values(bundle)) {
6
+ if (chunk.type === "chunk" && chunk.isEntry) {
7
+ chunk.code += `${iifeReturnValueName};`;
8
+ }
9
+ }
10
+ }
11
+ };
12
+ }
@@ -1,7 +1,6 @@
1
1
  export * from './devHtmlPrerender';
2
2
  export * from './devServerGlobals';
3
3
  export * from './download';
4
- export * from './multipageMove';
5
4
  export * from './resolveVirtualModules';
6
5
  export * from './tsconfigPaths';
7
6
  export * from './noopBackground';
@@ -14,3 +13,4 @@ export * from './defineImportMeta';
14
13
  export * from './removeEntrypointMainFunction';
15
14
  export * from './wxtPluginLoader';
16
15
  export * from './resolveAppConfig';
16
+ export * from './iifeFooter';
@@ -1,7 +1,6 @@
1
1
  export * from "./devHtmlPrerender.mjs";
2
2
  export * from "./devServerGlobals.mjs";
3
3
  export * from "./download.mjs";
4
- export * from "./multipageMove.mjs";
5
4
  export * from "./resolveVirtualModules.mjs";
6
5
  export * from "./tsconfigPaths.mjs";
7
6
  export * from "./noopBackground.mjs";
@@ -14,3 +13,4 @@ export * from "./defineImportMeta.mjs";
14
13
  export * from "./removeEntrypointMainFunction.mjs";
15
14
  export * from "./wxtPluginLoader.mjs";
16
15
  export * from "./resolveAppConfig.mjs";
16
+ export * from "./iifeFooter.mjs";
@@ -73,8 +73,10 @@ async function createServerInternal() {
73
73
  });
74
74
  await buildAndOpenBrowser();
75
75
  const reloadOnChange = createFileReloader(server);
76
- server.watcher.on("all", reloadOnChange);
77
- keyboardShortcuts.start();
76
+ server.watcher.on("all", async (...args) => {
77
+ await reloadOnChange(args[0], args[1]);
78
+ keyboardShortcuts.start();
79
+ });
78
80
  keyboardShortcuts.printHelp({
79
81
  canReopenBrowser: !wxt.config.runnerConfig.config.disabled && !!runner.canOpen?.()
80
82
  });
@@ -48,7 +48,7 @@ export async function initialize(options) {
48
48
  input.packageManager ??= options.packageManager;
49
49
  const isExists = await fs.pathExists(input.directory);
50
50
  if (isExists) {
51
- const isEmpty = (await fs.readdir(input.directory)).length === 0;
51
+ const isEmpty = (await fs.readdir(input.directory)).filter((dir) => dir !== ".git").length === 0;
52
52
  if (!isEmpty) {
53
53
  consola.error(
54
54
  `The directory ${path.resolve(input.directory)} is not empty. Aborted.`
@@ -10,8 +10,7 @@ export function createKeyboardShortcuts(server) {
10
10
  };
11
11
  return {
12
12
  start() {
13
- if (rl) return;
14
- rl = readline.createInterface({
13
+ rl ??= readline.createInterface({
15
14
  input: process.stdin,
16
15
  terminal: false
17
16
  // Don't intercept ctrl+C, ctrl+Z, etc
@@ -19,8 +18,7 @@ export function createKeyboardShortcuts(server) {
19
18
  rl.on("line", handleInput);
20
19
  },
21
20
  stop() {
22
- rl?.close();
23
- rl = void 0;
21
+ rl?.removeListener("line", handleInput);
24
22
  },
25
23
  printHelp(flags) {
26
24
  if (flags.canReopenBrowser) {
@@ -25,6 +25,7 @@ export function createWebExtRunner() {
25
25
  devtools: wxtUserConfig?.openDevtools,
26
26
  startUrl: wxtUserConfig?.startUrls,
27
27
  keepProfileChanges: wxtUserConfig?.keepProfileChanges,
28
+ chromiumPort: wxtUserConfig?.chromiumPort,
28
29
  ...wxt.config.browser === "firefox" ? {
29
30
  firefox: wxtUserConfig?.binaries?.firefox,
30
31
  firefoxProfile: wxtUserConfig?.firefoxProfile,
@@ -1,4 +1,4 @@
1
- import { getPublicFiles } from "../../utils/fs.mjs";
1
+ import { getPublicFiles } from "../fs.mjs";
2
2
  import fs from "fs-extra";
3
3
  import { dirname, resolve } from "path";
4
4
  import pc from "picocolors";
@@ -0,0 +1,15 @@
1
+ import { MinimatchOptions } from 'minimatch';
2
+ /**
3
+ * Run [`minimatch`](https://npmjs.com/package/minimatch) against multiple
4
+ * patterns.
5
+ *
6
+ * Supports negated patterns, the order does not matter. If your `search` string
7
+ * matches any of the negative patterns, it will return `false`.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
12
+ * minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
13
+ * ```
14
+ */
15
+ export declare function minimatchMultiple(search: string, patterns: string[] | undefined, options?: MinimatchOptions): boolean;
@@ -0,0 +1,17 @@
1
+ import { minimatch } from "minimatch";
2
+ export function minimatchMultiple(search, patterns, options) {
3
+ if (patterns == null) return false;
4
+ const negatePatterns = [];
5
+ const positivePatterns = [];
6
+ for (const pattern of patterns) {
7
+ if (pattern[0] === "!") negatePatterns.push(pattern.slice(1));
8
+ else positivePatterns.push(pattern);
9
+ }
10
+ if (negatePatterns.some(
11
+ (negatePattern) => minimatch(search, negatePattern, options)
12
+ ))
13
+ return false;
14
+ return positivePatterns.some(
15
+ (positivePattern) => minimatch(search, positivePattern, options)
16
+ );
17
+ }