wxt 0.20.20 → 0.20.22

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.
Files changed (33) hide show
  1. package/dist/builtin-modules/favicon-permission.mjs +28 -0
  2. package/dist/builtin-modules/index.mjs +2 -1
  3. package/dist/cli/cli-utils.mjs +1 -1
  4. package/dist/core/builders/vite/index.mjs +13 -6
  5. package/dist/core/builders/vite/plugins/download.mjs +1 -0
  6. package/dist/core/clean.mjs +4 -4
  7. package/dist/core/create-server.mjs +3 -5
  8. package/dist/core/generate-wxt-dir.mjs +18 -1
  9. package/dist/core/initialize.mjs +14 -14
  10. package/dist/core/keyboard-shortcuts.mjs +2 -2
  11. package/dist/core/resolve-config.mjs +7 -0
  12. package/dist/core/utils/building/build-entrypoints.mjs +3 -3
  13. package/dist/core/utils/building/find-entrypoints.mjs +24 -10
  14. package/dist/core/utils/building/internal-build.mjs +7 -7
  15. package/dist/core/utils/env.mjs +22 -13
  16. package/dist/core/utils/i18n.mjs +1 -1
  17. package/dist/core/utils/log/printFileList.mjs +14 -14
  18. package/dist/core/utils/log/printHeader.mjs +2 -2
  19. package/dist/core/utils/manifest.mjs +12 -13
  20. package/dist/core/utils/picomatch-multiple.mjs +26 -0
  21. package/dist/core/utils/syntax-errors.mjs +2 -2
  22. package/dist/core/utils/theme-icons.mjs +71 -0
  23. package/dist/core/utils/transform.mjs +1 -1
  24. package/dist/core/zip.mjs +3 -3
  25. package/dist/index.d.mts +2 -2
  26. package/dist/types.d.mts +89 -10
  27. package/dist/utils/content-script-ui/iframe.d.mts +2 -2
  28. package/dist/utils/inject-script.d.mts +2 -2
  29. package/dist/utils/inject-script.mjs +3 -2
  30. package/dist/version.mjs +1 -1
  31. package/package.json +27 -19
  32. package/LICENSE +0 -21
  33. package/dist/core/utils/minimatch-multiple.mjs +0 -26
@@ -0,0 +1,28 @@
1
+ import { defineWxtModule } from "../modules.mjs";
2
+ //#region src/builtin-modules/favicon-permission.ts
3
+ /**
4
+ * Adds a template-literal type for the `_favicon/` paths served by Chrome's
5
+ * favicon API when the `favicon` permission is declared. With this module,
6
+ * `browser.runtime.getURL('/_favicon/?pageUrl=...')` type-checks without
7
+ * needing a `@ts-expect-error`.
8
+ *
9
+ * Extensions that load favicons from a content script must still add their own
10
+ * `web_accessible_resources` entry — this module intentionally does not touch
11
+ * the manifest. See the review thread on #1570 for context.
12
+ *
13
+ * @see https://developer.chrome.com/docs/extensions/how-to/ui/favicons
14
+ */
15
+ var favicon_permission_default = defineWxtModule({
16
+ name: "wxt:built-in:favicon-permission",
17
+ setup(wxt) {
18
+ wxt.hooks.hook("prepare:publicPaths", (_, paths) => {
19
+ if (!wxt.config.manifest.permissions?.includes("favicon")) return;
20
+ paths.push({
21
+ type: "templateLiteral",
22
+ path: "_favicon/?${string}"
23
+ });
24
+ });
25
+ }
26
+ });
27
+ //#endregion
28
+ export { favicon_permission_default as default };
@@ -1,5 +1,6 @@
1
+ import favicon_permission_default from "./favicon-permission.mjs";
1
2
  import unimport_default from "./unimport.mjs";
2
3
  //#region src/builtin-modules/index.ts
3
- const builtinModules = [unimport_default];
4
+ const builtinModules = [unimport_default, favicon_permission_default];
4
5
  //#endregion
5
6
  export { builtinModules };
@@ -1,5 +1,5 @@
1
- import { formatDuration } from "../core/utils/time.mjs";
2
1
  import { filterTruthy, toArray } from "../core/utils/arrays.mjs";
2
+ import { formatDuration } from "../core/utils/time.mjs";
3
3
  import { registerWxt } from "../core/wxt.mjs";
4
4
  import { printHeader } from "../core/utils/log/printHeader.mjs";
5
5
  import "../core/utils/log/index.mjs";
@@ -1,6 +1,10 @@
1
1
  import { normalizePath } from "../../utils/paths.mjs";
2
2
  import "../../utils/index.mjs";
3
+ import { toArray } from "../../utils/arrays.mjs";
3
4
  import { getEntrypointBundlePath, isHtmlEntrypoint } from "../../utils/entrypoints.mjs";
5
+ import { createExtensionEnvironment } from "../../utils/environments/extension-environment.mjs";
6
+ import "../../utils/environments/index.mjs";
7
+ import { safeVarName } from "../../utils/strings.mjs";
4
8
  import { devHtmlPrerender } from "./plugins/devHtmlPrerender.mjs";
5
9
  import { devServerGlobals } from "./plugins/devServerGlobals.mjs";
6
10
  import { download } from "./plugins/download.mjs";
@@ -18,14 +22,10 @@ import { resolveAppConfig } from "./plugins/resolveAppConfig.mjs";
18
22
  import { iifeFooter } from "./plugins/iifeFooter.mjs";
19
23
  import { iifeAnonymous } from "./plugins/iifeAnonymous.mjs";
20
24
  import "./plugins/index.mjs";
21
- import { toArray } from "../../utils/arrays.mjs";
22
- import { safeVarName } from "../../utils/strings.mjs";
23
- import { createExtensionEnvironment } from "../../utils/environments/extension-environment.mjs";
24
- import "../../utils/environments/index.mjs";
25
25
  import { mkdir, readdir, rename, rmdir, stat } from "node:fs/promises";
26
26
  import { dirname, extname, join, relative } from "node:path";
27
- import { ViteNodeServer } from "vite-node/server";
28
27
  import { ViteNodeRunner } from "vite-node/client";
28
+ import { ViteNodeServer } from "vite-node/server";
29
29
  import { installSourcemapsSupport } from "vite-node/source-map";
30
30
  //#region src/core/builders/vite/index.ts
31
31
  async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
@@ -52,6 +52,10 @@ async function createViteBuilder(wxtConfig, hooks, getWxtDevServer) {
52
52
  config.server.watch = { ignored: [`${wxtConfig.outBaseDir}/**`, `${wxtConfig.wxtDir}/**`] };
53
53
  config.legacy ??= {};
54
54
  config.legacy.skipWebSocketTokenCheck = true;
55
+ if (isRolldownVersion(vite.version)) {} else {
56
+ config.esbuild ??= {};
57
+ if (config.esbuild) config.esbuild.charset = "ascii";
58
+ }
55
59
  const server = getWxtDevServer?.();
56
60
  config.plugins ??= [];
57
61
  config.plugins.push(download(wxtConfig), devHtmlPrerender(wxtConfig, server), resolveVirtualModules(wxtConfig), devServerGlobals(wxtConfig, server), tsconfigPaths(wxtConfig), noopBackground(), globals(wxtConfig), defineImportMeta(), wxtPluginLoader(wxtConfig), resolveAppConfig(wxtConfig));
@@ -263,7 +267,7 @@ function getRollupEntry(entrypoint) {
263
267
  return entrypoint.inputPath;
264
268
  }
265
269
  /**
266
- * Ensures the HTML files output by a multipage build are in the correct
270
+ * Ensures the HTML files output by a multi-page build are in the correct
267
271
  * location. This does two things:
268
272
  *
269
273
  * 1. Moves the HTML files to their final location at
@@ -310,5 +314,8 @@ async function removeEmptyDirs(dir) {
310
314
  await rmdir(dir);
311
315
  } catch {}
312
316
  }
317
+ function isRolldownVersion(version) {
318
+ return Number(version.split(".")[0]) >= 8;
319
+ }
313
320
  //#endregion
314
321
  export { createViteBuilder };
@@ -11,6 +11,7 @@ import { fetchCached } from "../../../utils/network.mjs";
11
11
  function download(config) {
12
12
  return {
13
13
  name: "wxt:download",
14
+ enforce: "pre",
14
15
  resolveId: {
15
16
  filter: { id: /^url:/ },
16
17
  handler(id) {
@@ -2,7 +2,7 @@ import { registerWxt, wxt } from "./wxt.mjs";
2
2
  import { rm } from "node:fs/promises";
3
3
  import { glob } from "tinyglobby";
4
4
  import path from "node:path";
5
- import pc from "picocolors";
5
+ import { styleText } from "node:util";
6
6
  //#region src/core/clean.ts
7
7
  async function clean(config) {
8
8
  if (typeof config === "string") config = { root: config };
@@ -15,7 +15,7 @@ async function clean(config) {
15
15
  "**/.wxt",
16
16
  `${path.relative(root, wxt.config.outBaseDir)}/*`
17
17
  ];
18
- wxt.logger.debug("Looking for:", tempDirs.map(pc.cyan).join(", "));
18
+ wxt.logger.debug("Looking for:", tempDirs.map((dir) => styleText("cyan", dir)).join(", "));
19
19
  const directories = await glob(tempDirs, {
20
20
  cwd: root,
21
21
  absolute: true,
@@ -27,13 +27,13 @@ async function clean(config) {
27
27
  wxt.logger.debug("No generated files found.");
28
28
  return;
29
29
  }
30
- wxt.logger.debug("Found:", directories.map((dir) => pc.cyan(path.relative(root, dir))).join(", "));
30
+ wxt.logger.debug("Found:", directories.map((dir) => styleText("cyan", path.relative(root, dir))).join(", "));
31
31
  for (const directory of directories) {
32
32
  await rm(directory, {
33
33
  force: true,
34
34
  recursive: true
35
35
  });
36
- wxt.logger.debug("Deleted " + pc.cyan(path.relative(root, directory)));
36
+ wxt.logger.debug("Deleted " + styleText("cyan", path.relative(root, directory)));
37
37
  }
38
38
  }
39
39
  //#endregion
@@ -12,7 +12,7 @@ import { createExtensionRunner } from "./runners/index.mjs";
12
12
  import { createKeyboardShortcuts } from "./keyboard-shortcuts.mjs";
13
13
  import { isBabelSyntaxError, logBabelSyntaxError } from "./utils/syntax-errors.mjs";
14
14
  import { relative } from "node:path";
15
- import pc from "picocolors";
15
+ import { styleText } from "node:util";
16
16
  import { debounce } from "perfect-debounce";
17
17
  import chokidar from "chokidar";
18
18
  import { Mutex } from "async-mutex";
@@ -172,7 +172,7 @@ function createFileReloader(server) {
172
172
  server.restartBrowser();
173
173
  return;
174
174
  }
175
- wxt.logger.info(`Changed: ${Array.from(new Set(fileChanges)).map((file) => pc.dim(relative(wxt.config.root, file))).join(", ")}`);
175
+ wxt.logger.info(`Changed: ${Array.from(new Set(fileChanges)).map((file) => styleText("dim", relative(wxt.config.root, file))).join(", ")}`);
176
176
  const allEntrypoints = await findEntrypoints();
177
177
  try {
178
178
  const { output: newOutput } = await rebuild(allEntrypoints, changes.rebuildGroups, changes.cachedOutput);
@@ -231,9 +231,7 @@ function reloadHtmlPages(groups, server) {
231
231
  return { reloadedNames: htmlEntries.map((entry) => entry.name) };
232
232
  }
233
233
  function getFilenameList(names) {
234
- return names.map((name) => {
235
- return pc.cyan(name);
236
- }).join(pc.dim(", "));
234
+ return names.map((name) => styleText("cyan", name)).join(styleText("dim", ", "));
237
235
  }
238
236
  /**
239
237
  * Based on the current build output, return a list of files that are:
@@ -39,7 +39,7 @@ async function getPathsDeclarationEntry(entrypoints) {
39
39
  const unions = [
40
40
  ` | ""`,
41
41
  ` | "/"`,
42
- ...paths.map(normalizePath).sort().map((path) => ` | "/${path}"`)
42
+ ...paths.map(normalizePublicPathEntry).sort((a, b) => a.path.localeCompare(b.path) || a.type.localeCompare(b.type)).map(renderPublicPathUnionMember)
43
43
  ].join("\n");
44
44
  return {
45
45
  path: "types/paths.d.ts",
@@ -59,6 +59,23 @@ declare module "wxt/browser" {
59
59
  tsReference: true
60
60
  };
61
61
  }
62
+ function normalizePublicPathEntry(entry) {
63
+ if (typeof entry === "string") return {
64
+ type: "string",
65
+ path: normalizePath(entry)
66
+ };
67
+ return {
68
+ type: entry.type,
69
+ path: normalizePath(entry.path)
70
+ };
71
+ }
72
+ function renderPublicPathUnionMember(entry) {
73
+ const path = entry.path.startsWith("/") ? entry.path : `/${entry.path}`;
74
+ switch (entry.type) {
75
+ case "templateLiteral": return ` | \`${path}\``;
76
+ case "string": return ` | "${path}"`;
77
+ }
78
+ }
62
79
  function getEntrypointPublicExt(entry) {
63
80
  if (isHtmlEntrypoint(entry)) return ".html";
64
81
  switch (entry.type) {
@@ -2,7 +2,7 @@ import { pathExists } from "./utils/fs.mjs";
2
2
  import { readdir, rename } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { consola as consola$1 } from "consola";
5
- import pc from "picocolors";
5
+ import { styleText } from "node:util";
6
6
  import prompts from "prompts";
7
7
  import { downloadTemplate } from "giget";
8
8
  //#region src/core/initialize.ts
@@ -22,7 +22,7 @@ async function initialize(options) {
22
22
  type: () => defaultTemplate == null ? "select" : void 0,
23
23
  message: "Choose a template",
24
24
  choices: templates.map((template) => ({
25
- title: TEMPLATE_COLORS[template.name]?.(template.name) ?? template.name,
25
+ title: TEMPLATE_COLORS[template.name] ? styleText(TEMPLATE_COLORS[template.name], template.name) : template.name,
26
26
  value: template
27
27
  }))
28
28
  },
@@ -32,19 +32,19 @@ async function initialize(options) {
32
32
  message: "Package Manager",
33
33
  choices: [
34
34
  {
35
- title: pc.red("npm"),
35
+ title: styleText("red", "npm"),
36
36
  value: "npm"
37
37
  },
38
38
  {
39
- title: pc.yellow("pnpm"),
39
+ title: styleText("yellow", "pnpm"),
40
40
  value: "pnpm"
41
41
  },
42
42
  {
43
- title: pc.cyan("yarn"),
43
+ title: styleText("cyan", "yarn"),
44
44
  value: "yarn"
45
45
  },
46
46
  {
47
- title: pc.magenta("bun"),
47
+ title: styleText("magenta", "bun"),
48
48
  value: "bun"
49
49
  }
50
50
  ]
@@ -62,12 +62,12 @@ async function initialize(options) {
62
62
  await cloneProject(input);
63
63
  const cdPath = path.relative(process.cwd(), path.resolve(input.directory));
64
64
  console.log();
65
- consola$1.log(`✨ WXT project created with the ${TEMPLATE_COLORS[input.template.name]?.(input.template.name) ?? input.template.name} template.`);
65
+ consola$1.log(`✨ WXT project created with the ${TEMPLATE_COLORS[input.template.name] ? styleText(TEMPLATE_COLORS[input.template.name], input.template.name) : input.template.name} template.`);
66
66
  console.log();
67
67
  consola$1.log("Next steps:");
68
68
  let step = 0;
69
- if (cdPath !== "") consola$1.log(` ${++step}.`, pc.cyan(`cd ${cdPath}`));
70
- consola$1.log(` ${++step}.`, pc.cyan(`${input.packageManager} install`));
69
+ if (cdPath !== "") consola$1.log(` ${++step}.`, styleText("cyan", `cd ${cdPath}`));
70
+ consola$1.log(` ${++step}.`, styleText("cyan", `${input.packageManager} install`));
71
71
  console.log();
72
72
  }
73
73
  async function listTemplates() {
@@ -109,11 +109,11 @@ async function cloneProject({ directory, template }) {
109
109
  }
110
110
  }
111
111
  const TEMPLATE_COLORS = {
112
- vanilla: pc.blue,
113
- vue: pc.green,
114
- react: pc.cyan,
115
- svelte: pc.red,
116
- solid: pc.blue
112
+ vanilla: "blue",
113
+ vue: "green",
114
+ react: "cyan",
115
+ svelte: "red",
116
+ solid: "blue"
117
117
  };
118
118
  const TEMPLATE_SORT_WEIGHT = {
119
119
  vanilla: 0,
@@ -1,5 +1,5 @@
1
1
  import { wxt } from "./wxt.mjs";
2
- import pc from "picocolors";
2
+ import { styleText } from "node:util";
3
3
  import readline from "node:readline";
4
4
  //#region src/core/keyboard-shortcuts.ts
5
5
  /** Function that creates a keyboard shortcut handler for the extension. */
@@ -21,7 +21,7 @@ function createKeyboardShortcuts(server) {
21
21
  rl?.removeListener("line", handleInput);
22
22
  },
23
23
  printHelp(flags) {
24
- if (flags.canReopenBrowser) wxt.logger.info(`${pc.dim("Press")} ${pc.bold("o + enter")} ${pc.dim("to reopen the browser")}`);
24
+ if (flags.canReopenBrowser) wxt.logger.info(`${styleText("dim", "Press")} ${styleText("bold", "o + enter")} ${styleText("dim", "to reopen the browser")}`);
25
25
  }
26
26
  };
27
27
  }
@@ -91,11 +91,16 @@ async function resolveConfig(inlineConfig, command) {
91
91
  const host = mergedConfig.dev?.server?.host ?? mergedConfig.dev?.server?.hostname ?? "localhost";
92
92
  let port = mergedConfig.dev?.server?.port;
93
93
  const origin = mergedConfig.dev?.server?.origin ?? mergedConfig.dev?.server?.hostname ?? "localhost";
94
+ const strictPort = mergedConfig.dev?.server?.strictPort ?? false;
94
95
  if (port == null || !isFinite(port)) port = await getPort({
95
96
  host,
96
97
  port: 3e3,
97
98
  portRange: [3001, 3010]
98
99
  });
100
+ else if (!strictPort) port = await getPort({
101
+ host,
102
+ port
103
+ });
99
104
  const originWithProtocolAndPort = [
100
105
  origin.match(/^https?:\/\//) ? "" : "http://",
101
106
  origin,
@@ -105,6 +110,7 @@ async function resolveConfig(inlineConfig, command) {
105
110
  host,
106
111
  port,
107
112
  origin: originWithProtocolAndPort,
113
+ strictPort,
108
114
  watchDebounce: safeStringToNumber(process.env.WXT_WATCH_DEBOUNCE) ?? 800
109
115
  };
110
116
  }
@@ -142,6 +148,7 @@ async function resolveConfig(inlineConfig, command) {
142
148
  userConfigMetadata: userConfigMetadata ?? {},
143
149
  alias,
144
150
  experimental: defu(mergedConfig.experimental, {}),
151
+ suppressWarnings: mergedConfig.suppressWarnings ?? {},
145
152
  dev: {
146
153
  server: devServerConfig,
147
154
  reloadCommand
@@ -3,15 +3,15 @@ import { wxt } from "../../wxt.mjs";
3
3
  import { getPublicFiles } from "../fs.mjs";
4
4
  import { copyFile, mkdir, writeFile } from "node:fs/promises";
5
5
  import { dirname, resolve } from "path";
6
- import pc from "picocolors";
6
+ import { styleText } from "node:util";
7
7
  //#region src/core/utils/building/build-entrypoints.ts
8
8
  async function buildEntrypoints(groups, spinner) {
9
9
  const steps = [];
10
10
  for (let i = 0; i < groups.length; i++) {
11
11
  const group = groups[i];
12
12
  const groupNames = toArray(group).map((e) => e.name);
13
- const groupNameColored = groupNames.join(pc.dim(", "));
14
- spinner.update({ text: pc.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNameColored}` });
13
+ const groupNameColored = groupNames.join(styleText("dim", ", "));
14
+ spinner.update({ text: styleText("dim", `[${i + 1}/${groups.length}]`) + ` ${groupNameColored}` });
15
15
  try {
16
16
  steps.push(await wxt.builder.build(group));
17
17
  } catch (err) {
@@ -5,10 +5,10 @@ import { wxt } from "../../wxt.mjs";
5
5
  import { mkdir, readFile, writeFile } from "node:fs/promises";
6
6
  import { glob } from "tinyglobby";
7
7
  import { relative, resolve } from "path";
8
+ import { styleText } from "node:util";
8
9
  import { parseHTML } from "linkedom";
9
10
  import { camelCase } from "scule";
10
- import pc from "picocolors";
11
- import { minimatch } from "minimatch";
11
+ import picomatch from "picomatch";
12
12
  import JSON5 from "json5";
13
13
  //#region src/core/utils/building/find-entrypoints.ts
14
14
  /**
@@ -31,7 +31,7 @@ async function findEntrypoints() {
31
31
  const entrypointInfos = relativePaths.reduce((results, relativePath) => {
32
32
  const inputPath = resolve(wxt.config.entrypointsDir, relativePath);
33
33
  const name = getEntrypointName(wxt.config.entrypointsDir, inputPath);
34
- const matchingGlob = pathGlobs.find((glob) => minimatch(relativePath, glob));
34
+ const matchingGlob = pathGlobs.find((glob) => picomatch(glob)(relativePath));
35
35
  if (matchingGlob) {
36
36
  const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
37
37
  results.push({
@@ -90,7 +90,7 @@ async function findEntrypoints() {
90
90
  await wxt.hooks.callHook("entrypoints:resolved", wxt, entrypoints);
91
91
  wxt.logger.debug("All entrypoints:", entrypoints);
92
92
  const skippedEntrypointNames = entrypoints.filter((item) => item.skipped).map((item) => item.name);
93
- if (skippedEntrypointNames.length) wxt.logger.warn(["The following entrypoints have been skipped:", ...skippedEntrypointNames.map((item) => `${pc.dim("-")} ${pc.cyan(item)}`)].join("\n"));
93
+ if (skippedEntrypointNames.length) wxt.logger.warn(["The following entrypoints have been skipped:", ...skippedEntrypointNames.map((item) => `${styleText("dim", "-")} ${styleText("cyan", item)}`)].join("\n"));
94
94
  return entrypoints;
95
95
  }
96
96
  /** Returns a map of input paths to the file's options. */
@@ -157,16 +157,30 @@ async function getPopupEntrypoint(info, options) {
157
157
  const strictOptions = resolvePerBrowserOptions({
158
158
  ...perBrowserOptions,
159
159
  defaultTitle: title,
160
- mv2Key: type
160
+ actionType: type
161
161
  }, wxt.config.browser);
162
- if (strictOptions.mv2Key && strictOptions.mv2Key !== "page_action") strictOptions.mv2Key = "browser_action";
162
+ if (strictOptions.actionType && strictOptions.actionType !== "page_action") strictOptions.actionType = "browser_action";
163
+ const opts = {
164
+ ...strictOptions,
165
+ themeIcons
166
+ };
167
+ let _actionType = opts.actionType;
168
+ Object.defineProperty(opts, "actionType", {
169
+ get: () => _actionType,
170
+ set: (v) => _actionType = v,
171
+ enumerable: true,
172
+ configurable: true
173
+ });
174
+ Object.defineProperty(opts, "mv2Key", {
175
+ get: () => _actionType,
176
+ set: (v) => _actionType = v,
177
+ enumerable: true,
178
+ configurable: true
179
+ });
163
180
  return {
164
181
  type: "popup",
165
182
  name: "popup",
166
- options: {
167
- ...strictOptions,
168
- themeIcons
169
- },
183
+ options: opts,
170
184
  inputPath: info.inputPath,
171
185
  outputDir: wxt.config.outDir
172
186
  };
@@ -10,8 +10,8 @@ import { ValidationError, validateEntrypoints } from "../validation.mjs";
10
10
  import { mkdir, rm } from "node:fs/promises";
11
11
  import { glob } from "tinyglobby";
12
12
  import { relative } from "node:path";
13
+ import { styleText } from "node:util";
13
14
  import { mergeJsonOutputs } from "@aklinker1/rollup-plugin-visualizer";
14
- import pc from "picocolors";
15
15
  import { isCI } from "ci-info";
16
16
  //#region src/core/utils/building/internal-build.ts
17
17
  /**
@@ -29,7 +29,7 @@ async function internalBuild() {
29
29
  await wxt.hooks.callHook("build:before", wxt);
30
30
  const verb = wxt.config.command === "serve" ? "Pre-rendering" : "Building";
31
31
  const target = `${wxt.config.browser}-mv${wxt.config.manifestVersion}`;
32
- wxt.logger.info(`${verb} ${pc.cyan(target)} for ${pc.cyan(wxt.config.mode)} with ${pc.green(`${wxt.builder.name} ${wxt.builder.version}`)}`);
32
+ wxt.logger.info(`${verb} ${styleText("cyan", target)} for ${styleText("cyan", wxt.config.mode)} with ${styleText("green", `${wxt.builder.name} ${wxt.builder.version}`)}`);
33
33
  const startTime = Date.now();
34
34
  await rm(wxt.config.outDir, {
35
35
  recursive: true,
@@ -50,10 +50,10 @@ async function internalBuild() {
50
50
  if (wxt.config.analysis.enabled) {
51
51
  await combineAnalysisStats();
52
52
  const statsPath = relative(wxt.config.root, wxt.config.analysis.outputFile);
53
- wxt.logger.info(`Analysis complete:\n ${pc.gray("└─")} ${pc.yellow(statsPath)}`);
54
- if (wxt.config.analysis.open) if (isCI) wxt.logger.debug(`Skipped opening ${pc.yellow(statsPath)} in CI`);
53
+ wxt.logger.info(`Analysis complete:\n ${styleText("gray", "└─")} ${styleText("yellow", statsPath)}`);
54
+ if (wxt.config.analysis.open) if (isCI) wxt.logger.debug(`Skipped opening ${styleText("yellow", statsPath)} in CI`);
55
55
  else {
56
- wxt.logger.info(`Opening ${pc.yellow(statsPath)} in browser...`);
56
+ wxt.logger.info(`Opening ${styleText("yellow", statsPath)} in browser...`);
57
57
  const { default: open } = await import("open");
58
58
  await open(wxt.config.analysis.outputFile);
59
59
  }
@@ -85,8 +85,8 @@ function printValidationResults({ errorCount, errors, warningCount }) {
85
85
  Array.from(entrypointErrors.entries()).forEach(([entrypoint, errors]) => {
86
86
  wxt.logger.log(relative(cwd, entrypoint.inputPath) + "\n");
87
87
  errors.forEach((err) => {
88
- const type = err.type === "error" ? pc.red("ERROR") : pc.yellow("WARN");
89
- const received = pc.dim(`(received: ${JSON.stringify(err.value)})`);
88
+ const type = err.type === "error" ? styleText("red", "ERROR") : styleText("yellow", "WARN");
89
+ const received = styleText("dim", `(received: ${JSON.stringify(err.value)})`);
90
90
  wxt.logger.log(` - ${type} ${err.message} ${received}`);
91
91
  });
92
92
  console.log();
@@ -1,21 +1,30 @@
1
- import { config } from "dotenv";
2
1
  import { expand } from "dotenv-expand";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { parseEnv } from "node:util";
3
4
  //#region src/core/utils/env.ts
4
5
  /** Load environment files based on the current mode and browser. */
5
6
  function loadEnv(mode, browser) {
6
- return expand(config({
7
- quiet: true,
8
- path: [
9
- `.env.${mode}.${browser}.local`,
10
- `.env.${mode}.${browser}`,
11
- `.env.${browser}.local`,
12
- `.env.${browser}`,
13
- `.env.${mode}.local`,
14
- `.env.${mode}`,
15
- `.env.local`,
16
- `.env`
17
- ]
7
+ const envFiles = [
8
+ `.env`,
9
+ `.env.local`,
10
+ `.env.${mode}`,
11
+ `.env.${mode}.local`,
12
+ `.env.${browser}`,
13
+ `.env.${browser}.local`,
14
+ `.env.${mode}.${browser}`,
15
+ `.env.${mode}.${browser}.local`
16
+ ];
17
+ const parsed = Object.fromEntries(envFiles.flatMap((filePath) => {
18
+ if (!existsSync(filePath)) return [];
19
+ try {
20
+ const parsedEnv = parseEnv(readFileSync(filePath, "utf-8"));
21
+ return Object.entries(parsedEnv);
22
+ } catch {
23
+ return [];
24
+ }
18
25
  }));
26
+ expand({ parsed });
27
+ return parsed;
19
28
  }
20
29
  //#endregion
21
30
  export { loadEnv };
@@ -2,7 +2,7 @@
2
2
  const predefinedMessages = {
3
3
  "@@extension_id": {
4
4
  message: "<browser.runtime.id>",
5
- description: "The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even unlocalized extensions can use this message.\nNote: You can't use this message in a manifest file."
5
+ description: "The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even non-localized extensions can use this message.\nNote: You can't use this message in a manifest file."
6
6
  },
7
7
  "@@ui_locale": {
8
8
  message: "<browser.i18n.getUiLocale()>",
@@ -1,7 +1,7 @@
1
1
  import { printTable } from "./printTable.mjs";
2
2
  import { lstat } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import pc from "picocolors";
4
+ import { styleText } from "node:util";
5
5
  import { filesize } from "filesize";
6
6
  //#region src/core/utils/log/printFileList.ts
7
7
  async function printFileList(log, header, baseDir, files) {
@@ -9,26 +9,26 @@ async function printFileList(log, header, baseDir, files) {
9
9
  const fileRows = await Promise.all(files.map(async (file, i) => {
10
10
  const parts = [path.relative(process.cwd(), baseDir) + path.sep, path.relative(baseDir, file)];
11
11
  const prefix = i === files.length - 1 ? " └─" : " ├─";
12
- const color = getChunkColor(file);
12
+ const chunkColor = getChunkColor(file);
13
13
  const stats = await lstat(file);
14
14
  totalSize += stats.size;
15
15
  const size = String(filesize(stats.size));
16
- return [`${pc.gray(prefix)} ${pc.dim(parts[0])}${color(parts[1])}`, pc.dim(size)];
16
+ return [`${styleText("gray", prefix)} ${styleText("dim", parts[0])}${styleText(chunkColor, parts[1])}`, styleText("dim", size)];
17
17
  }));
18
- fileRows.push([`${pc.cyan("Σ Total size:")} ${String(filesize(totalSize))}`]);
18
+ fileRows.push([`${styleText("cyan", "Σ Total size:")} ${String(filesize(totalSize))}`]);
19
19
  printTable(log, header, fileRows);
20
20
  }
21
- const DEFAULT_COLOR = pc.blue;
21
+ const DEFAULT_COLOR = "blue";
22
22
  const CHUNK_COLORS = {
23
- ".js.map": pc.gray,
24
- ".cjs.map": pc.gray,
25
- ".mjs.map": pc.gray,
26
- ".html": pc.green,
27
- ".css": pc.magenta,
28
- ".js": pc.cyan,
29
- ".cjs": pc.cyan,
30
- ".mjs": pc.cyan,
31
- ".zip": pc.yellow
23
+ ".js.map": "gray",
24
+ ".cjs.map": "gray",
25
+ ".mjs.map": "gray",
26
+ ".html": "green",
27
+ ".css": "magenta",
28
+ ".js": "cyan",
29
+ ".cjs": "cyan",
30
+ ".mjs": "cyan",
31
+ ".zip": "yellow"
32
32
  };
33
33
  function getChunkColor(filename) {
34
34
  return Object.entries(CHUNK_COLORS).find(([key]) => filename.endsWith(key))?.[1] ?? DEFAULT_COLOR;
@@ -1,9 +1,9 @@
1
1
  import { version } from "../../../version.mjs";
2
2
  import { consola as consola$1 } from "consola";
3
- import pc from "picocolors";
3
+ import { styleText } from "node:util";
4
4
  //#region src/core/utils/log/printHeader.ts
5
5
  function printHeader() {
6
- consola$1.log(`\n${pc.gray("WXT")} ${pc.gray(pc.bold(version))}`);
6
+ consola$1.log(`\n${styleText("gray", "WXT")} ${styleText(["bold", "grey"], version)}`);
7
7
  }
8
8
  //#endregion
9
9
  export { printHeader };
@@ -5,6 +5,7 @@ import { writeFileIfDifferent } from "./fs.mjs";
5
5
  import { ContentSecurityPolicy } from "./content-security-policy.mjs";
6
6
  import { hashContentScriptOptions, mapWxtOptionsToContentScript } from "./content-scripts.mjs";
7
7
  import { getPackageJson } from "./package.mjs";
8
+ import { addDiscoveredThemeIcons } from "./theme-icons.mjs";
8
9
  import { mkdir } from "node:fs/promises";
9
10
  import { resolve } from "path";
10
11
  import defu from "defu";
@@ -54,7 +55,9 @@ async function generateManifest(allEntrypoints, buildOutput) {
54
55
  }
55
56
  manifest.version = version;
56
57
  manifest.version_name = wxt.config.browser === "firefox" || versionName === version ? void 0 : versionName;
58
+ if (wxt.config.browser === "firefox" && !userManifest.browser_specific_settings?.gecko?.data_collection_permissions && !wxt.config.suppressWarnings?.firefoxDataCollection) wxt.logger.warn("Firefox requires `data_collection_permissions` for new extensions from November 3, 2025. Existing extensions are exempt for now.\nFor more details, see: https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/\nTo suppress this warning, set `suppressWarnings.firefoxDataCollection` to `true` in your wxt config.\n");
57
59
  addEntrypoints(manifest, entrypoints, buildOutput);
60
+ if (wxt.config.browser === "firefox") addDiscoveredThemeIcons(manifest, buildOutput);
58
61
  if (wxt.config.command === "serve") addDevModeCsp(manifest);
59
62
  if (wxt.config.command === "serve") addDevModePermissions(manifest);
60
63
  await wxt.hooks.callHook("build:manifestGenerated", wxt, manifest);
@@ -136,25 +139,18 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
136
139
  if (popup.options.browserStyle) options.browser_style = popup.options.browserStyle;
137
140
  if (popup.options.defaultArea) options.default_area = popup.options.defaultArea;
138
141
  if (popup.options.themeIcons) options.theme_icons = popup.options.themeIcons;
139
- if (manifest.manifest_version === 3) manifest.action = {
140
- ...manifest.action,
142
+ const actionKey = manifest.manifest_version === 2 ? popup.options.actionType ?? "browser_action" : wxt.config.browser === "firefox" ? popup.options.actionType ?? "action" : "action";
143
+ manifest[actionKey] = {
144
+ ...manifest[actionKey],
141
145
  ...options,
142
146
  default_popup
143
147
  };
144
- else {
145
- const key = popup.options.mv2Key ?? "browser_action";
146
- manifest[key] = {
147
- ...manifest[key],
148
- ...options,
149
- default_popup
150
- };
151
- }
152
148
  }
153
149
  if (devtools) manifest.devtools_page = getEntrypointBundlePath(devtools, wxt.config.outDir, ".html");
154
150
  if (options) {
155
151
  const page = getEntrypointBundlePath(options, wxt.config.outDir, ".html");
156
152
  manifest.options_ui = {
157
- open_in_tab: options.options.openInTab,
153
+ open_in_tab: options.options.openInTab ?? false,
158
154
  browser_style: wxt.config.browser === "firefox" ? options.options.browserStyle : void 0,
159
155
  chrome_style: wxt.config.browser !== "firefox" ? options.options.chromeStyle : void 0,
160
156
  page
@@ -363,13 +359,15 @@ function stripKeys(manifest) {
363
359
  if (wxt.config.manifestVersion === 2) {
364
360
  keysToRemove.push(...mv3OnlyKeys);
365
361
  if (wxt.config.browser === "firefox") keysToRemove.push(...firefoxMv3OnlyKeys);
366
- } else keysToRemove.push(...mv2OnlyKeys);
362
+ } else {
363
+ keysToRemove.push(...mv2OnlyKeys);
364
+ if (wxt.config.browser !== "firefox") keysToRemove.push(...chromeMv2OnlyKeys);
365
+ }
367
366
  keysToRemove.forEach((key) => {
368
367
  delete manifest[key];
369
368
  });
370
369
  }
371
370
  const mv2OnlyKeys = [
372
- "page_action",
373
371
  "browser_action",
374
372
  "automation",
375
373
  "content_capabilities",
@@ -393,6 +391,7 @@ const mv3OnlyKeys = [
393
391
  "optional_host_permissions",
394
392
  "side_panel"
395
393
  ];
394
+ const chromeMv2OnlyKeys = ["page_action"];
396
395
  const firefoxMv3OnlyKeys = ["host_permissions"];
397
396
  const DEFAULT_MV3_EXTENSION_PAGES_CSP = "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';";
398
397
  const DEFAULT_MV3_SANDBOX_CSP = "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';";
@@ -0,0 +1,26 @@
1
+ import picomatch from "picomatch";
2
+ //#region src/core/utils/picomatch-multiple.ts
3
+ /**
4
+ * Run [`picomatch`](https://npmjs.com/package/picomatch) against multiple
5
+ * patterns.
6
+ *
7
+ * Supports negated patterns, the order does not matter. If your `search` string
8
+ * matches any of the negative patterns, it will return `false`.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * picomatchMultiple('a.json', ['*.json', '!b.json']); // => true
13
+ * picomatchMultiple('b.json', ['*.json', '!b.json']); // => false
14
+ * ```;
15
+ */
16
+ function picomatchMultiple(search, patterns, options) {
17
+ if (patterns == null) return false;
18
+ const negatePatterns = [];
19
+ const positivePatterns = [];
20
+ for (const pattern of patterns) if (pattern[0] === "!") negatePatterns.push(pattern.slice(1));
21
+ else positivePatterns.push(pattern);
22
+ if (negatePatterns.some((negatePattern) => picomatch(negatePattern, options)(search))) return false;
23
+ return positivePatterns.some((positivePattern) => picomatch(positivePattern, options)(search));
24
+ }
25
+ //#endregion
26
+ export { picomatchMultiple };
@@ -1,6 +1,6 @@
1
1
  import { wxt } from "../wxt.mjs";
2
2
  import { relative } from "node:path";
3
- import pc from "picocolors";
3
+ import { styleText } from "node:util";
4
4
  //#region src/core/utils/syntax-errors.ts
5
5
  function isBabelSyntaxError(error) {
6
6
  return error instanceof SyntaxError && error.code === "BABEL_PARSER_SYNTAX_ERROR";
@@ -9,7 +9,7 @@ function logBabelSyntaxError(error) {
9
9
  let filename = relative(wxt.config.root, error.id);
10
10
  if (filename.startsWith("..")) filename = error.id;
11
11
  let message = error.message.replace(/\(\d+:\d+\)$/, `(${filename}:${error.loc.line}:${error.loc.column + 1})`);
12
- if (error.frame) message += "\n\n" + pc.red(error.frame);
12
+ if (error.frame) message += "\n\n" + styleText("red", error.frame);
13
13
  wxt.logger.error(message);
14
14
  }
15
15
  //#endregion
@@ -0,0 +1,71 @@
1
+ import { normalizePath } from "./paths.mjs";
2
+ import { wxt } from "../wxt.mjs";
3
+ //#region src/core/utils/theme-icons.ts
4
+ /**
5
+ * Firefox only.
6
+ *
7
+ * If the manifest has an `action` (MV3) or `browser_action` (MV2) and the user
8
+ * has not already set `theme_icons` on it, discover light/dark icon pairs from
9
+ * the public assets and attach them.
10
+ */
11
+ function addDiscoveredThemeIcons(manifest, buildOutput) {
12
+ const action = manifest.action ?? manifest.browser_action;
13
+ if (action == null) return;
14
+ if (action.theme_icons != null) return;
15
+ const themeIcons = discoverThemeIcons(buildOutput);
16
+ if (themeIcons == null) return;
17
+ action.theme_icons = themeIcons;
18
+ }
19
+ /**
20
+ * Scan `publicAssets` for paired `-light`/`-dark` icon files and return the
21
+ * sizes where both variants exist, sorted ascending. Returns `undefined` if no
22
+ * complete pairs are found so callers can short-circuit.
23
+ */
24
+ function discoverThemeIcons(buildOutput) {
25
+ const bySize = /* @__PURE__ */ new Map();
26
+ for (const asset of buildOutput.publicAssets) for (const regex of themeIconRegex) {
27
+ const match = asset.fileName.match(regex);
28
+ if (match?.groups == null) continue;
29
+ const size = Number(match.groups.size);
30
+ const variant = match.groups.variant;
31
+ const entry = bySize.get(size) ?? {};
32
+ const incoming = normalizePath(asset.fileName);
33
+ const existing = entry[variant];
34
+ if (existing != null && existing !== incoming) {
35
+ wxt.logger.warn(`Multiple theme icon files matched size ${size} variant "${variant}": keeping "${existing}", ignoring "${incoming}". Use a single naming pattern per (size, variant).`);
36
+ break;
37
+ }
38
+ entry[variant] = incoming;
39
+ bySize.set(size, entry);
40
+ break;
41
+ }
42
+ const pairs = [];
43
+ for (const [size, entry] of bySize) {
44
+ if (entry.light != null && entry.dark != null) {
45
+ pairs.push({
46
+ light: entry.light,
47
+ dark: entry.dark,
48
+ size
49
+ });
50
+ continue;
51
+ }
52
+ const present = entry.light != null ? "light" : "dark";
53
+ const missing = entry.light != null ? "dark" : "light";
54
+ const file = entry.light ?? entry.dark;
55
+ wxt.logger.warn(`Skipping theme icon size ${size}: found ${present} variant ("${file}") but no matching ${missing} variant. Add the missing file to include this size in theme_icons.`);
56
+ }
57
+ pairs.sort((a, b) => a.size - b.size);
58
+ return pairs.length > 0 ? pairs : void 0;
59
+ }
60
+ const themeIconRegex = [
61
+ /^icon-(?<variant>light|dark)-(?<size>[0-9]+)\.png$/,
62
+ /^icon-(?<variant>light|dark)-(?<size>[0-9]+)x[0-9]+\.png$/,
63
+ /^icon-(?<size>[0-9]+)-(?<variant>light|dark)\.png$/,
64
+ /^icon-(?<size>[0-9]+)x[0-9]+-(?<variant>light|dark)\.png$/,
65
+ /^icons?[/\\](?<variant>light|dark)-(?<size>[0-9]+)\.png$/,
66
+ /^icons?[/\\](?<variant>light|dark)-(?<size>[0-9]+)x[0-9]+\.png$/,
67
+ /^icons?[/\\](?<size>[0-9]+)-(?<variant>light|dark)\.png$/,
68
+ /^icons?[/\\](?<size>[0-9]+)x[0-9]+-(?<variant>light|dark)\.png$/
69
+ ];
70
+ //#endregion
71
+ export { addDiscoveredThemeIcons };
@@ -137,7 +137,7 @@ function removeSideEffectImports(mod) {
137
137
  }
138
138
  /**
139
139
  * Util to get the AST as a simple JSON object, stripping out large objects and
140
- * file locations to keep it readible
140
+ * file locations to keep it readable.
141
141
  */
142
142
  function getSimpleAstJson(ast) {
143
143
  if (!ast) return ast;
package/dist/core/zip.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { normalizePath } from "./utils/paths.mjs";
2
2
  import "./utils/index.mjs";
3
- import { formatDuration } from "./utils/time.mjs";
4
3
  import { safeFilename } from "./utils/strings.mjs";
4
+ import { formatDuration } from "./utils/time.mjs";
5
5
  import { registerWxt, wxt } from "./wxt.mjs";
6
6
  import { findEntrypoints } from "./utils/building/find-entrypoints.mjs";
7
7
  import { printFileList } from "./utils/log/printFileList.mjs";
@@ -9,7 +9,7 @@ import "./utils/log/index.mjs";
9
9
  import { getPackageJson } from "./utils/package.mjs";
10
10
  import { internalBuild } from "./utils/building/internal-build.mjs";
11
11
  import "./utils/building/index.mjs";
12
- import { minimatchMultiple } from "./utils/minimatch-multiple.mjs";
12
+ import { picomatchMultiple } from "./utils/picomatch-multiple.mjs";
13
13
  import { mkdir, readFile } from "node:fs/promises";
14
14
  import { glob } from "tinyglobby";
15
15
  import path from "node:path";
@@ -69,7 +69,7 @@ async function zipDir(directory, outputPath, options) {
69
69
  onlyFiles: true,
70
70
  expandDirectories: false
71
71
  })).filter((relativePath) => {
72
- return minimatchMultiple(relativePath, options?.include) || !minimatchMultiple(relativePath, options?.exclude);
72
+ return picomatchMultiple(relativePath, options?.include) || !picomatchMultiple(relativePath, options?.exclude);
73
73
  }), ...(options?.additionalFiles ?? []).map((file) => path.relative(directory, file))];
74
74
  for (const file of filesToZip) {
75
75
  const absolutePath = path.resolve(directory, file);
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, ExtensionRunnerConfig, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, ReloadContentScriptPayload, ResolvedBasePublicFile, ResolvedConfig, ResolvedEslintrc, ResolvedPerBrowserOptions, ResolvedPublicFile, ServerInfo, SidepanelEntrypoint, SidepanelEntrypointOptions, TargetBrowser, TargetManifestVersion, ThemeIcon, UnlistedScriptDefinition, UnlistedScriptEntrypoint, UserConfig, UserManifest, UserManifestFn, WebExtConfig, Wxt, WxtBuilder, WxtBuilderServer, WxtCommand, WxtDevServer, WxtDirEntry, WxtDirFileEntry, WxtDirTypeReferenceEntry, WxtHooks, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig } from "./types.mjs";
1
+ import { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, ExtensionRunnerConfig, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PublicPathEntry, ReloadContentScriptPayload, ResolvedBasePublicFile, ResolvedConfig, ResolvedEslintrc, ResolvedPerBrowserOptions, ResolvedPublicFile, ServerInfo, SidepanelEntrypoint, SidepanelEntrypointOptions, TargetBrowser, TargetManifestVersion, ThemeIcon, UnlistedScriptDefinition, UnlistedScriptEntrypoint, UserConfig, UserManifest, UserManifestFn, WebExtConfig, Wxt, WxtBuilder, WxtBuilderServer, WxtCommand, WxtDevServer, WxtDirEntry, WxtDirFileEntry, WxtDirTypeReferenceEntry, WxtHooks, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig } from "./types.mjs";
2
2
  import { build } from "./core/build.mjs";
3
3
  import { clean } from "./core/clean.mjs";
4
4
  import { defineConfig } from "./core/define-config.mjs";
@@ -9,4 +9,4 @@ import { prepare } from "./core/prepare.mjs";
9
9
  import { zip } from "./core/zip.mjs";
10
10
  import { normalizePath } from "./core/utils/paths.mjs";
11
11
  import { version } from "./version.mjs";
12
- export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, ExtensionRunnerConfig, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, ReloadContentScriptPayload, ResolvedBasePublicFile, ResolvedConfig, ResolvedEslintrc, ResolvedPerBrowserOptions, ResolvedPublicFile, ServerInfo, SidepanelEntrypoint, SidepanelEntrypointOptions, TargetBrowser, TargetManifestVersion, ThemeIcon, UnlistedScriptDefinition, UnlistedScriptEntrypoint, UserConfig, UserManifest, UserManifestFn, WebExtConfig, Wxt, WxtBuilder, WxtBuilderServer, WxtCommand, WxtDevServer, WxtDirEntry, WxtDirFileEntry, WxtDirTypeReferenceEntry, WxtHooks, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig, build, clean, createServer, defineConfig, defineRunnerConfig, defineWebExtConfig, initialize, normalizePath, prepare, version, zip };
12
+ export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, ExtensionRunnerConfig, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PublicPathEntry, ReloadContentScriptPayload, ResolvedBasePublicFile, ResolvedConfig, ResolvedEslintrc, ResolvedPerBrowserOptions, ResolvedPublicFile, ServerInfo, SidepanelEntrypoint, SidepanelEntrypointOptions, TargetBrowser, TargetManifestVersion, ThemeIcon, UnlistedScriptDefinition, UnlistedScriptEntrypoint, UserConfig, UserManifest, UserManifestFn, WebExtConfig, Wxt, WxtBuilder, WxtBuilderServer, WxtCommand, WxtDevServer, WxtDirEntry, WxtDirFileEntry, WxtDirTypeReferenceEntry, WxtHooks, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig, build, clean, createServer, defineConfig, defineRunnerConfig, defineWebExtConfig, initialize, normalizePath, prepare, version, zip };
package/dist/types.d.mts CHANGED
@@ -164,6 +164,25 @@ interface InlineConfig {
164
164
  * function that returns an object or promise.
165
165
  */
166
166
  manifest?: UserManifest | Promise<UserManifest> | UserManifestFn;
167
+ /**
168
+ * Suppress specific warnings during the build process.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * export default defineConfig({
173
+ * suppressWarnings: {
174
+ * firefoxDataCollection: true,
175
+ * },
176
+ * })
177
+ * ```;
178
+ */
179
+ suppressWarnings?: {
180
+ /**
181
+ * Suppress warnings for:
182
+ * https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent
183
+ */
184
+ firefoxDataCollection?: boolean;
185
+ };
167
186
  /**
168
187
  * Configure browser startup. Options set here can be overridden in a
169
188
  * `web-ext.config.ts` file.
@@ -233,7 +252,7 @@ interface InlineConfig {
233
252
  */
234
253
  sourcesRoot?: string;
235
254
  /**
236
- * [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to
255
+ * [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to
237
256
  * include when creating a ZIP of all your source code for Firefox. Patterns
238
257
  * are relative to your `config.zip.sourcesRoot`.
239
258
  *
@@ -247,7 +266,7 @@ interface InlineConfig {
247
266
  */
248
267
  includeSources?: string[];
249
268
  /**
250
- * [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to
269
+ * [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to
251
270
  * exclude when creating a ZIP of all your source code for Firefox. Patterns
252
271
  * are relative to your `config.zip.sourcesRoot`.
253
272
  *
@@ -260,7 +279,7 @@ interface InlineConfig {
260
279
  */
261
280
  excludeSources?: string[];
262
281
  /**
263
- * [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to
282
+ * [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to
264
283
  * exclude when zipping the extension.
265
284
  *
266
285
  * @example
@@ -389,6 +408,14 @@ interface InlineConfig {
389
408
  * @default 'http://localhost:3000'
390
409
  */
391
410
  origin?: string;
411
+ /**
412
+ * Whether the dev server should fail if the specified port is already in
413
+ * use. When `false` and a `port` is specified, the next available port
414
+ * will be used instead of throwing an error.
415
+ *
416
+ * @default false
417
+ */
418
+ strictPort?: boolean;
392
419
  /**
393
420
  * Hostname to run the dev server on.
394
421
  *
@@ -430,8 +457,8 @@ interface InlineConfig {
430
457
  * set in WXT's config instead of Vite's.
431
458
  *
432
459
  * This is a function because any vite plugins added need to be recreated for
433
- * each individual build step, incase they have internal state causing them to
434
- * fail when reused.
460
+ * each individual build step, in case they have internal state causing them
461
+ * to fail when reused.
435
462
  */
436
463
  vite?: (env: ConfigEnv) => WxtViteConfig | Promise<WxtViteConfig>;
437
464
  }
@@ -703,7 +730,15 @@ interface ThemeIcon {
703
730
  size: number;
704
731
  }
705
732
  interface PopupEntrypointOptions extends BaseEntrypointOptions {
706
- /** Defaults to "browser_action" to be equivalent to MV3's "action" key */
733
+ /**
734
+ * The type of action to use in the manifest.
735
+ *
736
+ * In MV2, defaults to `"browser_action"`. In MV3, `"browser_action"` is
737
+ * converted to `"action"`, while `"page_action"` is kept as-is (Firefox MV3
738
+ * only).
739
+ */
740
+ actionType?: PerBrowserOption<'browser_action' | 'page_action'>;
741
+ /** @deprecated Use `actionType` instead. */
707
742
  mv2Key?: PerBrowserOption<'browser_action' | 'page_action'>;
708
743
  defaultIcon?: Record<string, string>;
709
744
  defaultTitle?: PerBrowserOption<string>;
@@ -872,16 +907,42 @@ type PerBrowserMap<T> = {
872
907
  * `PerBrowserOption`, like `defaultIcon`.
873
908
  */
874
909
  type ResolvedPerBrowserOptions<T, TOmitted extends keyof T = never> = { [key in keyof Omit<T, TOmitted>]: T[key] extends PerBrowserOption<infer U> ? U : T[key] } & { [key in TOmitted]: T[key] };
910
+ /**
911
+ * Firefox data collection permission types for personal data. See:
912
+ * https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/#specifying-data-types
913
+ */
914
+ type FirefoxDataCollectionType = 'locationInfo' | 'browsingActivity' | 'websiteContent' | 'websiteActivity' | 'searchTerms' | 'bookmarksInfo' | 'healthInfo' | 'contactInfo' | 'socialInfo' | (string & {});
915
+ /**
916
+ * Firefox data collection permissions configuration. See:
917
+ * https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/#specifying-data-types
918
+ */
919
+ interface FirefoxDataCollectionPermissions {
920
+ /**
921
+ * Required data collection permissions. Users must opt in to use the
922
+ * extension. Can include personal data types or "none" to explicitly indicate
923
+ * no data collection.
924
+ */
925
+ required?: Array<FirefoxDataCollectionType | 'none'>;
926
+ /**
927
+ * Optional data collection permissions. Users can opt in after installation.
928
+ * Can include personal data types or "technicalAndInteraction" (which can
929
+ * only be optional).
930
+ */
931
+ optional?: Array<FirefoxDataCollectionType | 'technicalAndInteraction'>;
932
+ }
875
933
  /**
876
934
  * Manifest customization available in the `wxt.config.ts` file. You cannot
877
935
  * configure entrypoints here, they are configured inline.
878
936
  */
879
937
  type UserManifest = { [key in keyof Browser.runtime.ManifestV3 as key extends 'action' | 'background' | 'chrome_url_overrides' | 'devtools_page' | 'manifest_version' | 'options_page' | 'options_ui' | 'permissions' | 'sandbox' | 'web_accessible_resources' ? never : key]?: Browser.runtime.ManifestV3[key] } & {
880
938
  action?: Browser.runtime.ManifestV3['action'] & {
881
- browser_style?: boolean;
939
+ default_area?: 'navbar' | 'menupanel' | 'tabstrip' | 'personaltoolbar';
940
+ theme_icons?: ThemeIcon[];
882
941
  };
883
942
  browser_action?: Browser.runtime.ManifestV2['browser_action'] & {
884
943
  browser_style?: boolean;
944
+ default_area?: 'navbar' | 'menupanel' | 'tabstrip' | 'personaltoolbar';
945
+ theme_icons?: ThemeIcon[];
885
946
  };
886
947
  page_action?: Browser.runtime.ManifestV2['page_action'] & {
887
948
  browser_style?: boolean;
@@ -892,6 +953,11 @@ type UserManifest = { [key in keyof Browser.runtime.ManifestV3 as key extends 'a
892
953
  strict_min_version?: string;
893
954
  strict_max_version?: string;
894
955
  update_url?: string;
956
+ /**
957
+ * Firefox data collection permissions configuration. See:
958
+ * https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/#specifying-data-types
959
+ */
960
+ data_collection_permissions?: FirefoxDataCollectionPermissions;
895
961
  };
896
962
  gecko_android?: {
897
963
  strict_min_version?: string;
@@ -1080,14 +1146,18 @@ interface WxtHooks {
1080
1146
  *
1081
1147
  * @example
1082
1148
  * wxt.hooks.hook('prepare:publicPaths', (wxt, paths) => {
1083
- * paths.push('/icons/128.png');
1149
+ * paths.push('icons/128.png');
1150
+ * paths.push({
1151
+ * type: 'templateLiteral',
1152
+ * path: '_favicon/?${string}',
1153
+ * });
1084
1154
  * });
1085
1155
  *
1086
1156
  * @param wxt The configured WXT object
1087
1157
  * @param paths This list of paths TypeScript allows `browser.runtime.getURL`
1088
1158
  * to be called with.
1089
1159
  */
1090
- 'prepare:publicPaths': (wxt: Wxt, paths: string[]) => HookResult;
1160
+ 'prepare:publicPaths': (wxt: Wxt, paths: PublicPathEntry[]) => HookResult;
1091
1161
  /**
1092
1162
  * Called before the build is started in both dev mode and build mode.
1093
1163
  *
@@ -1296,11 +1366,16 @@ interface ResolvedConfig$1 {
1296
1366
  /** Import aliases to absolute paths. */
1297
1367
  alias: Record<string, string>;
1298
1368
  experimental: {};
1369
+ /** List of warning identifiers to suppress during the build process. */
1370
+ suppressWarnings: {
1371
+ firefoxDataCollection?: boolean;
1372
+ };
1299
1373
  dev: {
1300
1374
  /** Only defined during dev command */server?: {
1301
1375
  host: string;
1302
1376
  port: number;
1303
1377
  origin: string;
1378
+ strictPort: boolean;
1304
1379
  /**
1305
1380
  * The milliseconds to debounce when a file is saved before reloading. The
1306
1381
  * only way to set this option is to set the `WXT_WATCH_DEBOUNCE`
@@ -1490,6 +1565,10 @@ interface GeneratedPublicFile extends ResolvedBasePublicFile {
1490
1565
  /** Text to write to the file. */
1491
1566
  contents: string;
1492
1567
  }
1568
+ type PublicPathEntry = string | {
1569
+ type: 'string' | 'templateLiteral';
1570
+ path: string;
1571
+ };
1493
1572
  type WxtPlugin = () => void;
1494
1573
  type WxtDirEntry = WxtDirTypeReferenceEntry | WxtDirFileEntry;
1495
1574
  /**
@@ -1517,4 +1596,4 @@ interface WxtDirFileEntry {
1517
1596
  tsReference?: boolean;
1518
1597
  }
1519
1598
  //#endregion
1520
- export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, ExtensionRunnerConfig, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, ReloadContentScriptPayload, ResolvedBasePublicFile, ResolvedConfig$1 as ResolvedConfig, ResolvedEslintrc, ResolvedPerBrowserOptions, ResolvedPublicFile, ServerInfo, SidepanelEntrypoint, SidepanelEntrypointOptions, TargetBrowser, TargetManifestVersion, ThemeIcon, UnlistedScriptDefinition, UnlistedScriptEntrypoint, UserConfig, UserManifest, UserManifestFn, WebExtConfig, Wxt, WxtBuilder, WxtBuilderServer, WxtCommand, WxtDevServer, WxtDirEntry, WxtDirFileEntry, WxtDirTypeReferenceEntry, WxtHooks, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig };
1599
+ export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, ExtensionRunnerConfig, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PublicPathEntry, ReloadContentScriptPayload, ResolvedBasePublicFile, ResolvedConfig$1 as ResolvedConfig, ResolvedEslintrc, ResolvedPerBrowserOptions, ResolvedPublicFile, ServerInfo, SidepanelEntrypoint, SidepanelEntrypointOptions, TargetBrowser, TargetManifestVersion, ThemeIcon, UnlistedScriptDefinition, UnlistedScriptEntrypoint, UserConfig, UserManifest, UserManifestFn, WebExtConfig, Wxt, WxtBuilder, WxtBuilderServer, WxtCommand, WxtDevServer, WxtDirEntry, WxtDirFileEntry, WxtDirTypeReferenceEntry, WxtHooks, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig };
@@ -1,6 +1,6 @@
1
1
  import { ContentScriptContext } from "../content-script-context.mjs";
2
2
  import { ContentScriptUi, ContentScriptUiOptions } from "./types.mjs";
3
- import * as wxt_browser0 from "wxt/browser";
3
+ import * as _$wxt_browser0 from "wxt/browser";
4
4
 
5
5
  //#region src/utils/content-script-ui/iframe.d.ts
6
6
  /**
@@ -20,7 +20,7 @@ type IframeContentScriptUiOptions<TMounted> = ContentScriptUiOptions<TMounted> &
20
20
  * The path to the HTML page that will be shown in the iframe. This string
21
21
  * is passed into `browser.runtime.getURL`.
22
22
  */
23
- page: wxt_browser0.HtmlPublicPath;
23
+ page: _$wxt_browser0.HtmlPublicPath;
24
24
  /**
25
25
  * Callback executed when mounting the UI. Use this function to customize
26
26
  * the iframe or wrapper element's appearance. It is called every time
@@ -1,7 +1,7 @@
1
- import * as wxt_browser0 from "wxt/browser";
1
+ import * as _$wxt_browser0 from "wxt/browser";
2
2
 
3
3
  //#region src/utils/inject-script.d.ts
4
- type ScriptPublicPath = Extract<wxt_browser0.PublicPath, `${string}.js`>;
4
+ type ScriptPublicPath = Extract<_$wxt_browser0.PublicPath, `${string}.js`>;
5
5
  /**
6
6
  * This function can only be called inside content scripts.
7
7
  *
@@ -15,9 +15,10 @@ import { browser } from "wxt/browser";
15
15
  async function injectScript(path, options) {
16
16
  const url = browser.runtime.getURL(path);
17
17
  const script = document.createElement("script");
18
- if (browser.runtime.getManifest().manifest_version === 2) script.text = await fetch(url).then((res) => res.text());
18
+ const isManifestV2 = browser.runtime.getManifest().manifest_version === 2;
19
+ if (isManifestV2) script.text = await fetch(url).then((res) => res.text());
19
20
  else script.src = url;
20
- const loadedPromise = makeLoadedPromise(script);
21
+ const loadedPromise = isManifestV2 ? void 0 : makeLoadedPromise(script);
21
22
  await options?.modifyScript?.(script);
22
23
  (document.head ?? document.documentElement).append(script);
23
24
  if (!options?.keepInDom) script.remove();
package/dist/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region src/version.ts
2
- const version = "0.20.20";
2
+ const version = "0.20.22";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,15 +1,27 @@
1
1
  {
2
2
  "name": "wxt",
3
3
  "type": "module",
4
- "version": "0.20.20",
4
+ "version": "0.20.22",
5
5
  "description": "⚡ Next-gen Web Extension Framework",
6
6
  "license": "MIT",
7
+ "scripts": {
8
+ "wxt": "bun run src/cli/index.ts",
9
+ "build": "buildc -- bun run tsdown --config-loader unrun",
10
+ "check": "bun run build && bun run --sequential 'check:*'",
11
+ "check:default": "check",
12
+ "check:tsc-virtual": "tsc --noEmit -p src/virtual",
13
+ "test": "buildc --deps-only -- vitest",
14
+ "test:coverage": "bun run test run --coverage",
15
+ "sync-releases": "pnpx changelogen@latest gh release",
16
+ "prepack": "bun run build"
17
+ },
7
18
  "dependencies": {
8
19
  "@1natsu/wait-element": "^4.1.2",
9
20
  "@aklinker1/rollup-plugin-visualizer": "5.12.0",
10
21
  "@webext-core/fake-browser": "^1.3.4",
11
22
  "@webext-core/isolated-element": "^1.1.3",
12
23
  "@webext-core/match-patterns": "^1.0.3",
24
+ "@wxt-dev/browser": "^0.1.40",
13
25
  "@wxt-dev/storage": "^1.0.0",
14
26
  "async-mutex": "^0.5.0",
15
27
  "c12": "^3.3.3",
@@ -18,20 +30,18 @@
18
30
  "ci-info": "^4.4.0",
19
31
  "consola": "^3.4.2",
20
32
  "defu": "^6.1.4",
21
- "dotenv": "^17.3.1",
22
33
  "dotenv-expand": "^12.0.3",
23
34
  "esbuild": "^0.27.1",
24
- "filesize": "^11.0.13",
35
+ "filesize": "^11.0.15",
25
36
  "get-port-please": "^3.2.0",
26
37
  "giget": "^1.2.3 || ^2.0.0 || ^3.0.0",
27
- "hookable": "^6.0.1",
38
+ "hookable": "^6.1.0",
28
39
  "import-meta-resolve": "^4.2.0",
29
40
  "is-wsl": "^3.1.1",
30
41
  "json5": "^2.2.3",
31
42
  "jszip": "^3.10.1",
32
43
  "linkedom": "^0.18.12",
33
44
  "magicast": "^0.5.2",
34
- "minimatch": "^10.2.4",
35
45
  "nano-spawn": "^2.0.0",
36
46
  "nanospinner": "^1.2.2",
37
47
  "normalize-path": "^3.0.0",
@@ -39,7 +49,7 @@
39
49
  "ohash": "^2.0.11",
40
50
  "open": "^11.0.0",
41
51
  "perfect-debounce": "^2.1.0",
42
- "picocolors": "^1.1.1",
52
+ "picomatch": "^4.0.3",
43
53
  "prompts": "^2.4.2",
44
54
  "publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.4",
45
55
  "scule": "^1.3.0",
@@ -47,8 +57,7 @@
47
57
  "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0 || ^6.0.0",
48
58
  "vite": "^5.4.19 || ^6.3.4 || ^7.0.0 || ^8.0.0-0",
49
59
  "vite-node": "^3.2.4 || ^5.0.0 || ^6.0.0",
50
- "web-ext-run": "^0.2.4",
51
- "@wxt-dev/browser": "^0.1.38"
60
+ "web-ext-run": "^0.2.4"
52
61
  },
53
62
  "peerDependencies": {
54
63
  "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0"
@@ -59,17 +68,22 @@
59
68
  }
60
69
  },
61
70
  "devDependencies": {
71
+ "@aklinker1/buildc": "^1.1.7",
62
72
  "@faker-js/faker": "^10.3.0",
73
+ "@types/bun": "^1.3.5",
74
+ "@types/fs-extra": "^11.0.4",
63
75
  "@types/lodash.merge": "^4.6.9",
64
76
  "@types/node": "^20.17.6",
65
77
  "@types/normalize-path": "^3.0.2",
78
+ "@types/picomatch": "^4.0.2",
66
79
  "@types/prompts": "^2.4.9",
67
- "eslint": "^10.0.0",
80
+ "eslint": "^10.1.0",
68
81
  "extract-zip": "^2.0.1",
69
82
  "happy-dom": "^20.8.3",
70
83
  "lodash.merge": "^4.6.2",
71
84
  "oxlint": "^1.51.0",
72
85
  "publint": "^0.3.18",
86
+ "tsdown": "^0.21.0",
73
87
  "typescript": "^5.9.3",
74
88
  "vitest": "^4.0.18",
75
89
  "vitest-plugin-random-seed": "^1.1.2"
@@ -192,14 +206,8 @@
192
206
  "default": "./dist/modules.mjs"
193
207
  }
194
208
  },
195
- "scripts": {
196
- "wxt": "tsx src/cli/index.ts",
197
- "build": "buildc -- tsdown --config-loader unrun",
198
- "check": "pnpm build && pnpm run --reporter-hide-prefix /^check:.*/",
199
- "check:default": "check",
200
- "check:tsc-virtual": "tsc --noEmit -p src/virtual",
201
- "test": "buildc --deps-only -- vitest",
202
- "test:coverage": "pnpm test run --coverage",
203
- "sync-releases": "pnpx changelogen@latest gh release"
209
+ "engines": {
210
+ "node": ">=20.12.0",
211
+ "bun": ">=1.2.0"
204
212
  }
205
- }
213
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Aaron
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,26 +0,0 @@
1
- import { minimatch } from "minimatch";
2
- //#region src/core/utils/minimatch-multiple.ts
3
- /**
4
- * Run [`minimatch`](https://npmjs.com/package/minimatch) against multiple
5
- * patterns.
6
- *
7
- * Supports negated patterns, the order does not matter. If your `search` string
8
- * matches any of the negative patterns, it will return `false`.
9
- *
10
- * @example
11
- * ```ts
12
- * minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
13
- * minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
14
- * ```;
15
- */
16
- function minimatchMultiple(search, patterns, options) {
17
- if (patterns == null) return false;
18
- const negatePatterns = [];
19
- const positivePatterns = [];
20
- for (const pattern of patterns) if (pattern[0] === "!") negatePatterns.push(pattern.slice(1));
21
- else positivePatterns.push(pattern);
22
- if (negatePatterns.some((negatePattern) => minimatch(search, negatePattern, options))) return false;
23
- return positivePatterns.some((positivePattern) => minimatch(search, positivePattern, options));
24
- }
25
- //#endregion
26
- export { minimatchMultiple };