wxt 0.21.3 → 0.21.4

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 (36) hide show
  1. package/dist/builtin-modules/unimport.mjs +3 -3
  2. package/dist/cli/cli-utils.mjs +5 -2
  3. package/dist/core/builders/vite/index.mjs +8 -1
  4. package/dist/core/builders/vite/plugins/devHtmlPrerender.mjs +2 -2
  5. package/dist/core/initialize.mjs +1 -1
  6. package/dist/core/keyboard-shortcuts.mjs +1 -0
  7. package/dist/core/package-managers/bun.mjs +5 -2
  8. package/dist/core/package-managers/npm.mjs +10 -4
  9. package/dist/core/package-managers/pnpm.mjs +5 -2
  10. package/dist/core/package-managers/yarn.mjs +5 -2
  11. package/dist/core/resolve-config.mjs +14 -16
  12. package/dist/core/utils/building/find-entrypoints.mjs +3 -3
  13. package/dist/core/utils/building/rebuild.mjs +1 -1
  14. package/dist/core/utils/fs.mjs +6 -1
  15. package/dist/core/utils/log/index.mjs +1 -0
  16. package/dist/core/utils/log/printFileList.mjs +3 -3
  17. package/dist/core/utils/log/wxtLogger.mjs +32 -0
  18. package/dist/core/utils/manifest.mjs +6 -6
  19. package/dist/core/utils/paths.mjs +2 -2
  20. package/dist/core/utils/spinner.mjs +69 -0
  21. package/dist/core/utils/strings.mjs +2 -2
  22. package/dist/index.d.mts +2 -2
  23. package/dist/inline/get-port-please/index.mjs +196 -0
  24. package/dist/inline/is-wsl/index.mjs +44 -0
  25. package/dist/inline/normalize-path/index.mjs +23 -0
  26. package/dist/inline/ohash/index.mjs +113 -0
  27. package/dist/inline/scule/index.mjs +50 -0
  28. package/dist/types.d.mts +29 -32
  29. package/dist/utils/content-script-ui/iframe.d.mts +2 -0
  30. package/dist/utils/content-script-ui/iframe.mjs +2 -0
  31. package/dist/utils/content-script-ui/integrated.d.mts +2 -0
  32. package/dist/utils/content-script-ui/integrated.mjs +2 -0
  33. package/dist/utils/content-script-ui/shadow-root.d.mts +2 -0
  34. package/dist/utils/content-script-ui/shadow-root.mjs +2 -0
  35. package/dist/version.mjs +1 -1
  36. package/package.json +8 -9
@@ -55,13 +55,13 @@ async function getImportsModuleEntry(wxt, unimport) {
55
55
  tsReference: true
56
56
  };
57
57
  }
58
- async function getEslintConfigEntry(unimport, version, options) {
58
+ async function getEslintConfigEntry(unimport, configVersion, options) {
59
59
  const globals = (await unimport.getImports()).map((i) => i.as ?? i.name).filter(Boolean).sort().reduce((globals, name) => {
60
60
  globals[name] = options.eslintrc.globalsPropValue;
61
61
  return globals;
62
62
  }, {});
63
- if (version <= 8) return getEslint8ConfigEntry(options, globals);
64
- else return getEslint9ConfigEntry(options, globals);
63
+ if (configVersion === 8) return getEslint8ConfigEntry(options, globals);
64
+ return getEslint9ConfigEntry(options, globals);
65
65
  }
66
66
  function getEslint8ConfigEntry(options, globals) {
67
67
  return {
@@ -4,7 +4,7 @@ import { formatDuration } from "../core/utils/time.mjs";
4
4
  import { printHeader } from "../core/utils/log/printHeader.mjs";
5
5
  import "../core/utils/log/index.mjs";
6
6
  import { ValidationError } from "../core/utils/validation.mjs";
7
- import spawn from "nano-spawn";
7
+ import { x } from "tinyexec";
8
8
  import consola, { LogLevels } from "consola";
9
9
  //#region src/cli/cli-utils.ts
10
10
  /**
@@ -47,7 +47,10 @@ function createAliasedCommand(base, name, alias, bin, docsUrl) {
47
47
  const aliasedCommand = base.command(`${name} [...args]`, `Alias for ${alias} (${docsUrl})`).allowUnknownOptions().action(async () => {
48
48
  try {
49
49
  await registerWxt("build");
50
- await spawn(bin, process.argv.slice(process.argv.indexOf(aliasedCommand.name) + 1), { stdio: "inherit" });
50
+ await x(bin, process.argv.slice(process.argv.indexOf(aliasedCommand.name) + 1), {
51
+ throwOnError: true,
52
+ nodeOptions: { stdio: "inherit" }
53
+ });
51
54
  } catch {
52
55
  process.exit(1);
53
56
  }
@@ -347,7 +347,14 @@ async function removeEmptyDirs(dir) {
347
347
  const files = await readdir(dir);
348
348
  for (const file of files) {
349
349
  const filePath = join(dir, file);
350
- if ((await stat(filePath)).isDirectory()) await removeEmptyDirs(filePath);
350
+ let stats;
351
+ try {
352
+ stats = await stat(filePath);
353
+ } catch (err) {
354
+ if (err?.code === "ENOENT") continue;
355
+ throw err;
356
+ }
357
+ if (stats.isDirectory()) await removeEmptyDirs(filePath);
351
358
  }
352
359
  try {
353
360
  await rmdir(dir);
@@ -1,9 +1,9 @@
1
1
  import { normalizePath } from "../../../utils/paths.mjs";
2
2
  import { getEntrypointName } from "../../../utils/entrypoints.mjs";
3
3
  import "../../../utils/index.mjs";
4
+ import { c } from "../../../../inline/ohash/index.mjs";
4
5
  import { dirname, relative, resolve } from "node:path";
5
6
  import { parseHTML } from "linkedom";
6
- import { hash } from "ohash";
7
7
  //#region src/core/builders/vite/plugins/devHtmlPrerender.ts
8
8
  const inlineScriptContents = {};
9
9
  /**
@@ -48,7 +48,7 @@ function devHtmlPrerender(config, server) {
48
48
  const { document } = parseHTML(await server.transformHtml(url, html, originalUrl));
49
49
  document.querySelectorAll("script:not([src])").forEach((script) => {
50
50
  const textContent = script.textContent ?? "";
51
- const key = hash(textContent);
51
+ const key = c(textContent);
52
52
  inlineScriptContents[key] = textContent;
53
53
  const virtualScript = document.createElement("script");
54
54
  virtualScript.type = "module";
@@ -1,4 +1,5 @@
1
1
  import { pathExists } from "./utils/fs.mjs";
2
+ import { createSpinner } from "./utils/spinner.mjs";
2
3
  import { readdir, rename } from "node:fs/promises";
3
4
  import path from "node:path";
4
5
  import { consola as consola$1 } from "consola";
@@ -80,7 +81,6 @@ async function listTemplatesGithub() {
80
81
  return await res.json();
81
82
  }
82
83
  async function cloneProject({ directory, template }) {
83
- const { createSpinner } = await import("nanospinner");
84
84
  const spinner = createSpinner("Downloading template").start();
85
85
  try {
86
86
  await downloadTemplate(`gh:${REPO}/${template.path}`, {
@@ -11,6 +11,7 @@ function createKeyboardShortcuts(server) {
11
11
  return {
12
12
  start() {
13
13
  this.stop();
14
+ if (!process.stdin.isTTY) return;
14
15
  rl ??= readline.createInterface({
15
16
  input: process.stdin,
16
17
  terminal: false
@@ -1,5 +1,5 @@
1
1
  import { dedupeDependencies, npm } from "./npm.mjs";
2
- import spawn from "nano-spawn";
2
+ import { x } from "tinyexec";
3
3
  //#region src/core/package-managers/bun.ts
4
4
  const bun = {
5
5
  overridesKey: "overrides",
@@ -9,7 +9,10 @@ const bun = {
9
9
  async listDependencies(options) {
10
10
  const args = ["pm", "ls"];
11
11
  if (options?.all) args.push("--all");
12
- return dedupeDependencies((await spawn("bun", args, { cwd: options?.cwd })).stdout.split("\n").slice(1).map((line) => line.trim()).map((line) => /.* (@?\S+)@(\S+)$/.exec(line)).filter((match) => !!match).map(([_, name, version]) => ({
12
+ return dedupeDependencies((await x("bun", args, {
13
+ throwOnError: true,
14
+ nodeOptions: { cwd: options?.cwd }
15
+ })).stdout.split("\n").slice(1).map((line) => line.trim()).map((line) => /.* (@?\S+)@(\S+)$/.exec(line)).filter((match) => !!match).map(([_, name, version]) => ({
13
16
  name,
14
17
  version
15
18
  })));
@@ -1,23 +1,29 @@
1
1
  import { mkdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import spawn from "nano-spawn";
3
+ import { x } from "tinyexec";
4
4
  //#region src/core/package-managers/npm.ts
5
5
  const npm = {
6
6
  overridesKey: "overrides",
7
7
  async downloadDependency(id, downloadDir) {
8
8
  await mkdir(downloadDir, { recursive: true });
9
- const res = await spawn("npm", [
9
+ const res = await x("npm", [
10
10
  "pack",
11
11
  id,
12
12
  "--json"
13
- ], { cwd: downloadDir });
13
+ ], {
14
+ throwOnError: true,
15
+ nodeOptions: { cwd: downloadDir }
16
+ });
14
17
  const packed = JSON.parse(res.stdout);
15
18
  return path.resolve(downloadDir, packed[0].filename);
16
19
  },
17
20
  async listDependencies(options) {
18
21
  const args = ["ls", "--json"];
19
22
  if (options?.all) args.push("--depth", "Infinity");
20
- const res = await spawn("npm", args, { cwd: options?.cwd });
23
+ const res = await x("npm", args, {
24
+ throwOnError: true,
25
+ nodeOptions: { cwd: options?.cwd }
26
+ });
21
27
  return flattenNpmListOutput([JSON.parse(res.stdout)]);
22
28
  }
23
29
  };
@@ -1,5 +1,5 @@
1
1
  import { flattenNpmListOutput, npm } from "./npm.mjs";
2
- import spawn from "nano-spawn";
2
+ import { x } from "tinyexec";
3
3
  //#region src/core/package-managers/pnpm.ts
4
4
  const pnpm = {
5
5
  overridesKey: "resolutions",
@@ -14,7 +14,10 @@ const pnpm = {
14
14
  ];
15
15
  if (options?.all) args.push("--depth", "Infinity");
16
16
  if (typeof process !== "undefined" && process.env.WXT_PNPM_IGNORE_WORKSPACE === "true") args.push("--ignore-workspace");
17
- const res = await spawn("pnpm", args, { cwd: options?.cwd });
17
+ const res = await x("pnpm", args, {
18
+ throwOnError: true,
19
+ nodeOptions: { cwd: options?.cwd }
20
+ });
18
21
  return flattenNpmListOutput(JSON.parse(res.stdout));
19
22
  }
20
23
  };
@@ -1,5 +1,5 @@
1
1
  import { dedupeDependencies, npm } from "./npm.mjs";
2
- import spawn from "nano-spawn";
2
+ import { x } from "tinyexec";
3
3
  //#region src/core/package-managers/yarn.ts
4
4
  const yarn = {
5
5
  overridesKey: "resolutions",
@@ -9,7 +9,10 @@ const yarn = {
9
9
  async listDependencies(options) {
10
10
  const args = ["list", "--json"];
11
11
  if (options?.all) args.push("--depth", "Infinity");
12
- const tree = (await spawn("yarn", args, { cwd: options?.cwd })).stdout.split("\n").map((line) => JSON.parse(line)).find((line) => line.type === "tree")?.data;
12
+ const tree = (await x("yarn", args, {
13
+ throwOnError: true,
14
+ nodeOptions: { cwd: options?.cwd }
15
+ })).stdout.trimEnd().split("\n").map((line) => JSON.parse(line)).find((line) => line.type === "tree")?.data;
13
16
  if (tree == null) throw Error("'yarn list --json' did not output a tree");
14
17
  const queue = [...tree.trees];
15
18
  const dependencies = [];
@@ -5,18 +5,19 @@ import { createFsCache } from "./utils/cache.mjs";
5
5
  import { getEslintVersion } from "./utils/eslint.mjs";
6
6
  import { safeStringToNumber } from "./utils/number.mjs";
7
7
  import { loadEnv } from "./utils/env.mjs";
8
+ import { v } from "../inline/get-port-please/index.mjs";
8
9
  import { createSafariRunner } from "./runners/safari.mjs";
10
+ import { d } from "../inline/is-wsl/index.mjs";
9
11
  import { createWslRunner } from "./runners/wsl.mjs";
10
12
  import { createManualRunner } from "./runners/manual.mjs";
13
+ import { createWxtLogger } from "./utils/log/wxtLogger.mjs";
11
14
  import { pathExists } from "./utils/fs.mjs";
12
15
  import { glob } from "tinyglobby";
13
16
  import path from "node:path";
14
17
  import { loadConfig } from "c12";
15
18
  import consola, { LogLevels } from "consola";
16
19
  import defu from "defu";
17
- import { getPort } from "get-port-please";
18
20
  import { fileURLToPath, pathToFileURL } from "node:url";
19
- import isWsl from "is-wsl";
20
21
  //#region src/core/resolve-config.ts
21
22
  /**
22
23
  * Given an inline config, discover the config file if necessary, merge the
@@ -40,7 +41,7 @@ async function resolveConfig(inlineConfig, command) {
40
41
  }
41
42
  const mergedConfig = await mergeInlineConfig(inlineConfig, userConfig);
42
43
  const debug = mergedConfig.debug ?? false;
43
- const logger = mergedConfig.logger ?? consola;
44
+ const logger = createWxtLogger(mergedConfig.logger ?? consola);
44
45
  if (debug) logger.level = LogLevels.debug;
45
46
  const browser = mergedConfig.browser ?? "chrome";
46
47
  const targetBrowsers = mergedConfig.targetBrowsers ?? [];
@@ -93,12 +94,12 @@ async function resolveConfig(inlineConfig, command) {
93
94
  let port = mergedConfig.dev?.server?.port;
94
95
  const origin = mergedConfig.dev?.server?.origin ?? "localhost";
95
96
  const strictPort = mergedConfig.dev?.server?.strictPort ?? false;
96
- if (port == null || !isFinite(port)) port = await getPort({
97
+ if (port == null || !isFinite(port)) port = await v({
97
98
  host,
98
99
  port: 3e3,
99
100
  portRange: [3001, 3010]
100
101
  });
101
- else if (!strictPort) port = await getPort({
102
+ else if (!strictPort) port = await v({
102
103
  host,
103
104
  port
104
105
  });
@@ -141,7 +142,7 @@ async function resolveConfig(inlineConfig, command) {
141
142
  wxtModuleDir,
142
143
  root,
143
144
  webExt,
144
- runner: command === "serve" ? await resolveRunner(browser, logger, mergedConfig) : createManualRunner(),
145
+ runner: command === "serve" ? await resolveRunner(browser, logger, webExt.config) : createManualRunner(),
145
146
  srcDir,
146
147
  typesDir,
147
148
  wxtDir,
@@ -341,17 +342,14 @@ async function getUnimportOptions(wxtDir, srcDir, logger, config) {
341
342
  return defu(config.imports ?? {}, defaultOptions);
342
343
  }
343
344
  async function getUnimportEslintOptions(logger, wxtDir, options) {
344
- const inlineEnabled = options === false ? false : options?.eslintrc?.enabled ?? "auto";
345
+ const inlineEnabled = options === false ? false : options?.eslintrc?.enabled ?? true;
345
346
  const version = await getEslintVersion();
346
347
  const major = parseInt(version[0]);
347
348
  let enabled;
348
349
  switch (inlineEnabled) {
349
- case "auto":
350
+ case "auto": logger.warn(`\`imports.eslintrc.enabled: "auto"\` is deprecated. Use \`true\` instead.`);
350
351
  case true:
351
- if (isNaN(major)) {
352
- if (inlineEnabled === true) logger.warn("Could not determine installed ESLint version, `eslint-auto-imports.mjs` not generated");
353
- enabled = false;
354
- } else if (major <= 8) enabled = 8;
352
+ if (major <= 8) enabled = 8;
355
353
  else if (major >= 9) enabled = 9;
356
354
  else enabled = false;
357
355
  break;
@@ -372,7 +370,7 @@ async function isDirMissing(dir) {
372
370
  return !await pathExists(dir);
373
371
  }
374
372
  function logMissingDir(logger, name, expected) {
375
- logger.warn(`${name} directory not found: ./${normalizePath(path.relative(process.cwd(), expected))}`);
373
+ logger.warnOnce(`${name} directory not found: ./${normalizePath(path.relative(process.cwd(), expected))}`);
376
374
  }
377
375
  /** Map of `ConfigEnv` commands to their default modes. */
378
376
  const COMMAND_MODES = {
@@ -426,12 +424,12 @@ async function resolveWxtUserModules(root, modulesDir, modules = []) {
426
424
  }));
427
425
  return [...npmModules, ...localModules];
428
426
  }
429
- async function resolveRunner(browser, logger, mergedConfig) {
427
+ async function resolveRunner(browser, logger, webExt) {
430
428
  if (browser === "safari") return createSafariRunner();
431
- if (isWsl) return createWslRunner();
429
+ if (d) return createWslRunner();
432
430
  try {
433
431
  const { createWebExtRunner } = await import("./runners/web-ext.mjs");
434
- return mergedConfig.webExt?.disabled ? createManualRunner() : createWebExtRunner();
432
+ return webExt.disabled ? createManualRunner() : createWebExtRunner();
435
433
  } catch (err) {
436
434
  if (err?.code !== "ERR_MODULE_NOT_FOUND") throw err;
437
435
  logger.debug("Error loading the web-ext runner", err);
@@ -1,12 +1,12 @@
1
1
  import { CSS_EXTENSIONS_PATTERN } from "../paths.mjs";
2
2
  import { getEntrypointName, isHtmlEntrypoint, isJsEntrypoint, resolvePerBrowserOptions } from "../entrypoints.mjs";
3
+ import { s } from "../../../inline/scule/index.mjs";
3
4
  import { VIRTUAL_NOOP_BACKGROUND_MODULE_ID } from "../constants.mjs";
4
5
  import { wxt } from "../../wxt.mjs";
5
6
  import { mkdir, readFile, writeFile } from "node:fs/promises";
6
7
  import { glob } from "tinyglobby";
7
8
  import { relative, resolve } from "path";
8
9
  import { parseHTML } from "linkedom";
9
- import { camelCase } from "scule";
10
10
  import { styleText } from "node:util";
11
11
  import picomatch from "picomatch";
12
12
  import JSON5 from "json5";
@@ -118,8 +118,8 @@ async function importHtmlEntrypoint(info) {
118
118
  metaTags.forEach((tag) => {
119
119
  const name = tag.name;
120
120
  let key;
121
- if (name.startsWith("manifest.")) key = camelCase(name.slice(9));
122
- else if (name.startsWith("wxt.")) key = camelCase(name.slice(4));
121
+ if (name.startsWith("manifest.")) key = s(name.slice(9));
122
+ else if (name.startsWith("wxt.")) key = s(name.slice(4));
123
123
  else return;
124
124
  try {
125
125
  res[key] = JSON5.parse(tag.content);
@@ -1,8 +1,8 @@
1
1
  import { wxt } from "../../wxt.mjs";
2
2
  import { buildEntrypoints } from "./build-entrypoints.mjs";
3
+ import { createSpinner } from "../spinner.mjs";
3
4
  import { generateWxtDir } from "../../generate-wxt-dir.mjs";
4
5
  import { generateManifest, writeManifest } from "../manifest.mjs";
5
- import { createSpinner } from "nanospinner";
6
6
  //#region src/core/utils/building/rebuild.ts
7
7
  /**
8
8
  * Given a configuration, list of entrypoints, and an existing, partial output,
@@ -32,5 +32,10 @@ async function getPublicFiles() {
32
32
  expandDirectories: false
33
33
  })).map(unnormalizePath);
34
34
  }
35
+ function getBytesDisplay(bytes) {
36
+ if (bytes < 1e3) return `${bytes} B`;
37
+ if (bytes < 999995) return `${(bytes / 1e3).toFixed(2)} kB`;
38
+ return `${(bytes / 1e6).toFixed(2)} MB`;
39
+ }
35
40
  //#endregion
36
- export { getPublicFiles, pathExists, readJson, writeFileIfDifferent };
41
+ export { getBytesDisplay, getPublicFiles, pathExists, readJson, writeFileIfDifferent };
@@ -1,3 +1,4 @@
1
+ import "./wxtLogger.mjs";
1
2
  import "./printTable.mjs";
2
3
  import "./printFileList.mjs";
3
4
  import "./printBuildSummary.mjs";
@@ -1,9 +1,9 @@
1
1
  import { wxt } from "../../wxt.mjs";
2
+ import { getBytesDisplay } from "../fs.mjs";
2
3
  import { printTable } from "./printTable.mjs";
3
4
  import { lstat } from "node:fs/promises";
4
5
  import path from "node:path";
5
6
  import { styleText } from "node:util";
6
- import { filesize } from "filesize";
7
7
  //#region src/core/utils/log/printFileList.ts
8
8
  async function printFileList(log, header, baseDir, files) {
9
9
  let totalSize = 0;
@@ -15,13 +15,13 @@ async function printFileList(log, header, baseDir, files) {
15
15
  try {
16
16
  const stats = await lstat(file);
17
17
  totalSize += stats.size;
18
- size = String(filesize(stats.size));
18
+ size = getBytesDisplay(stats.size);
19
19
  } catch (ex) {
20
20
  wxt.logger.warn(`Could not get stats of '${file}' error: ${ex}`);
21
21
  }
22
22
  return [`${styleText("gray", prefix)} ${styleText("dim", parts[0])}${styleText(chunkColor, parts[1])}`, styleText("dim", size)];
23
23
  }));
24
- fileRows.push([`${styleText("cyan", "Σ Total size:")} ${String(filesize(totalSize))}`]);
24
+ fileRows.push([`${styleText("cyan", "Σ Total size:")} ${getBytesDisplay(totalSize)}`]);
25
25
  printTable(log, header, fileRows);
26
26
  }
27
27
  const DEFAULT_COLOR = "blue";
@@ -0,0 +1,32 @@
1
+ //#region src/core/utils/log/wxtLogger.ts
2
+ const warned = /* @__PURE__ */ new Set();
3
+ /**
4
+ * Wraps a `Logger` with a `warnOnce`. The set of already-warned messages is
5
+ * scoped to this wrapper instance, so it's reset whenever a new wrapper is
6
+ * created, e.g. once per `resolveConfig` call.
7
+ */
8
+ function createWxtLogger(logger) {
9
+ return {
10
+ get level() {
11
+ return logger.level;
12
+ },
13
+ set level(value) {
14
+ logger.level = value;
15
+ },
16
+ debug: logger.debug,
17
+ log: logger.log.bind(logger),
18
+ info: logger.info.bind(logger),
19
+ warn: logger.warn.bind(logger),
20
+ error: logger.error.bind(logger),
21
+ fatal: logger.fatal.bind(logger),
22
+ success: logger.success.bind(logger),
23
+ warnOnce: (...args) => {
24
+ const key = JSON.stringify(args);
25
+ if (warned.has(key)) return;
26
+ warned.add(key);
27
+ logger.warn(...args);
28
+ }
29
+ };
30
+ }
31
+ //#endregion
32
+ export { createWxtLogger };
@@ -28,7 +28,7 @@ async function generateManifest(allEntrypoints, buildOutput) {
28
28
  let versionName = wxt.config.manifest.version_name ?? wxt.config.manifest.version ?? pkg?.version;
29
29
  if (versionName == null) {
30
30
  versionName = "0.0.0";
31
- wxt.logger.warn("Extension version not found, defaulting to \"0.0.0\". Add a version to your `package.json` or `wxt.config.ts` file. For more details, see: https://wxt.dev/guide/key-concepts/manifest.html#version-and-version-name");
31
+ wxt.logger.warnOnce("Extension version not found, defaulting to \"0.0.0\". Add a version to your `package.json` or `wxt.config.ts` file. For more details, see: https://wxt.dev/guide/key-concepts/manifest.html#version-and-version-name");
32
32
  }
33
33
  const version = wxt.config.manifest.version ?? simplifyVersion(versionName);
34
34
  const baseManifest = {
@@ -42,7 +42,7 @@ async function generateManifest(allEntrypoints, buildOutput) {
42
42
  const userManifest = wxt.config.manifest;
43
43
  if (userManifest.manifest_version) {
44
44
  delete userManifest.manifest_version;
45
- wxt.logger.warn("`manifest.manifest_version` config was set, but ignored. To change the target manifest version, use the `manifestVersion` option or the `--mv2`/`--mv3` CLI flags.\nSee https://wxt.dev/guide/essentials/target-different-browsers.html#target-a-manifest-version");
45
+ wxt.logger.warnOnce("`manifest.manifest_version` config was set, but ignored. To change the target manifest version, use the `manifestVersion` option or the `--mv2`/`--mv3` CLI flags.\nSee https://wxt.dev/guide/essentials/target-different-browsers.html#target-a-manifest-version");
46
46
  }
47
47
  let manifest = defu(userManifest, baseManifest);
48
48
  if (wxt.config.command === "serve" && wxt.config.dev.reloadCommand) if (manifest.commands && Object.values(manifest.commands).filter((command) => command.suggested_key).length >= 4) warnings.push(["Extension already has 4 registered commands with suggested keys, WXT's reload command is disabled"]);
@@ -55,8 +55,8 @@ async function generateManifest(allEntrypoints, buildOutput) {
55
55
  }
56
56
  manifest.version = version;
57
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");
59
- if (wxt.config.browser === "firefox" && !manifest.browser_specific_settings?.gecko?.id && !wxt.config.suppressWarnings?.firefoxId) wxt.logger.warn("Firefox requires extension ID for MV3 and recommends it for MV2.\nFor more details, see: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_specific_settings#id");
58
+ if (wxt.config.browser === "firefox" && !userManifest.browser_specific_settings?.gecko?.data_collection_permissions && !wxt.config.suppressWarnings?.firefoxDataCollection) wxt.logger.warnOnce("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");
59
+ if (wxt.config.browser === "firefox" && !manifest.browser_specific_settings?.gecko?.id && !wxt.config.suppressWarnings?.firefoxId) wxt.logger.warnOnce("Firefox requires extension ID for MV3 and recommends it for MV2.\nFor more details, see: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_specific_settings#id");
60
60
  addEntrypoints(manifest, entrypoints, buildOutput);
61
61
  if (wxt.config.browser === "firefox") addDiscoveredThemeIcons(manifest, buildOutput);
62
62
  if (wxt.config.command === "serve") addDevModeCsp(manifest);
@@ -190,7 +190,7 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
190
190
  return map;
191
191
  }, /* @__PURE__ */ new Map());
192
192
  const manifestContentScripts = Array.from(hashToEntrypointsMap.values()).map((scripts) => mapWxtOptionsToContentScript(scripts[0].options, scripts.map((entry) => getEntrypointBundlePath(entry, wxt.config.outDir, ".js")), getContentScriptCssFiles(scripts, cssMap)));
193
- if (manifestContentScripts.length >= 0) {
193
+ if (manifestContentScripts.length > 0) {
194
194
  manifest.content_scripts ??= [];
195
195
  manifest.content_scripts.push(...manifestContentScripts);
196
196
  }
@@ -284,7 +284,7 @@ function getContentScriptCssWebAccessibleResources(contentScripts, contentScript
284
284
  if (cssFile == null) return;
285
285
  resources.push({
286
286
  resources: [cssFile],
287
- use_dynamic_url: true,
287
+ ...wxt.config.browser !== "firefox" && wxt.config.browser !== "safari" ? { use_dynamic_url: true } : {},
288
288
  matches: script.options.matches?.map((matchPattern) => stripPathFromMatchPattern(matchPattern)) ?? []
289
289
  });
290
290
  });
@@ -1,12 +1,12 @@
1
+ import normalize_path_default from "../../inline/normalize-path/index.mjs";
1
2
  import path from "node:path";
2
- import normalize from "normalize-path";
3
3
  //#region src/core/utils/paths.ts
4
4
  /**
5
5
  * Converts system paths to normalized bundler path. On Windows, this returns
6
6
  * paths with `/` instead of `\`.
7
7
  */
8
8
  function normalizePath(path) {
9
- return normalize(path);
9
+ return normalize_path_default(path);
10
10
  }
11
11
  /**
12
12
  * Given a normalized path, convert it to the system path style. On Windows,
@@ -0,0 +1,69 @@
1
+ import { styleText } from "node:util";
2
+ //#region src/core/utils/spinner.ts
3
+ function createSpinner(initialText = "", options = {}) {
4
+ const { color = "cyan" } = options;
5
+ const frames = [
6
+ "⠋",
7
+ "⠙",
8
+ "⠹",
9
+ "⠸",
10
+ "⠼",
11
+ "⠴",
12
+ "⠦",
13
+ "⠧",
14
+ "⠇",
15
+ "⠏"
16
+ ];
17
+ const FRAME_INTERVAL = 80;
18
+ let frameIndex = 0;
19
+ let timer = null;
20
+ let text = initialText;
21
+ function render() {
22
+ const frame = frames[frameIndex % frames.length];
23
+ const coloredFrame = color ? styleText(color, frame) : frame;
24
+ process.stdout.write(`\r${coloredFrame} ${text}\x1B[K`);
25
+ }
26
+ const spinner = {
27
+ start(startText) {
28
+ if (startText !== void 0) text = startText;
29
+ if (timer) return spinner;
30
+ process.stdout.write("\x1B[?25l");
31
+ render();
32
+ timer = setInterval(() => {
33
+ frameIndex++;
34
+ render();
35
+ }, FRAME_INTERVAL);
36
+ return spinner;
37
+ },
38
+ update(opts = {}) {
39
+ text = typeof opts === "string" ? opts : opts.text ?? text;
40
+ if (timer) render();
41
+ return spinner;
42
+ },
43
+ clear() {
44
+ if (timer) {
45
+ clearInterval(timer);
46
+ timer = null;
47
+ }
48
+ process.stdout.write("\r\x1B[K");
49
+ process.stdout.write("\x1B[?25h");
50
+ return spinner;
51
+ },
52
+ stop(finalText = "") {
53
+ spinner.clear();
54
+ if (finalText) process.stdout.write(finalText + "\n");
55
+ return spinner;
56
+ },
57
+ success(finalText) {
58
+ const message = finalText ?? text;
59
+ return spinner.stop(`${styleText("green", "✔")} ${message}`);
60
+ },
61
+ error(finalText) {
62
+ const message = finalText ?? text;
63
+ return spinner.stop(`${styleText("red", "✖")} ${message}`);
64
+ }
65
+ };
66
+ return spinner;
67
+ }
68
+ //#endregion
69
+ export { createSpinner };
@@ -1,11 +1,11 @@
1
- import { camelCase } from "scule";
1
+ import { s } from "../../inline/scule/index.mjs";
2
2
  //#region src/core/utils/strings.ts
3
3
  function kebabCaseAlphanumeric(str) {
4
4
  return str.toLowerCase().replace(/[^a-z0-9-\s]/g, "").replace(/\s+/g, "-");
5
5
  }
6
6
  /** Return a safe variable name for a given string. */
7
7
  function safeVarName(str) {
8
- const name = camelCase(kebabCaseAlphanumeric(str));
8
+ const name = s(kebabCaseAlphanumeric(str));
9
9
  if (name.match(/^[a-z]/)) return name;
10
10
  return "_" + name;
11
11
  }
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, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PrepareTsconfigs, 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";
1
+ import { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintConfigVersion, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PrepareTsconfigs, 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, WxtLogger, 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";
@@ -10,4 +10,4 @@ import { zip } from "./core/zip.mjs";
10
10
  import { normalizePath } from "./core/utils/paths.mjs";
11
11
  import { getEntrypointBundlePath } from "./core/utils/entrypoints.mjs";
12
12
  import { version } from "./version.mjs";
13
- export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PrepareTsconfigs, 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, defineWebExtConfig, getEntrypointBundlePath, initialize, normalizePath, prepare, version, zip };
13
+ export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintConfigVersion, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PrepareTsconfigs, 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, WxtLogger, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig, build, clean, createServer, defineConfig, defineWebExtConfig, getEntrypointBundlePath, initialize, normalizePath, prepare, version, zip };
@@ -0,0 +1,196 @@
1
+ import "node:fs/promises";
2
+ import "node:path";
3
+ import { createServer } from "node:net";
4
+ import { networkInterfaces } from "node:os";
5
+ //#region inline/get-port-please/index.mjs
6
+ const o = new Set([
7
+ 1,
8
+ 7,
9
+ 9,
10
+ 11,
11
+ 13,
12
+ 15,
13
+ 17,
14
+ 19,
15
+ 20,
16
+ 21,
17
+ 22,
18
+ 23,
19
+ 25,
20
+ 37,
21
+ 42,
22
+ 43,
23
+ 53,
24
+ 69,
25
+ 77,
26
+ 79,
27
+ 87,
28
+ 95,
29
+ 101,
30
+ 102,
31
+ 103,
32
+ 104,
33
+ 109,
34
+ 110,
35
+ 111,
36
+ 113,
37
+ 115,
38
+ 117,
39
+ 119,
40
+ 123,
41
+ 135,
42
+ 137,
43
+ 139,
44
+ 143,
45
+ 161,
46
+ 179,
47
+ 389,
48
+ 427,
49
+ 465,
50
+ 512,
51
+ 513,
52
+ 514,
53
+ 515,
54
+ 526,
55
+ 530,
56
+ 531,
57
+ 532,
58
+ 540,
59
+ 548,
60
+ 554,
61
+ 556,
62
+ 563,
63
+ 587,
64
+ 601,
65
+ 636,
66
+ 989,
67
+ 990,
68
+ 993,
69
+ 995,
70
+ 1719,
71
+ 1720,
72
+ 1723,
73
+ 2049,
74
+ 3659,
75
+ 4045,
76
+ 5060,
77
+ 5061,
78
+ 6e3,
79
+ 6566,
80
+ 6665,
81
+ 6666,
82
+ 6667,
83
+ 6668,
84
+ 6669,
85
+ 6697,
86
+ 10080
87
+ ]);
88
+ function s(e) {
89
+ return o.has(e);
90
+ }
91
+ function c(e) {
92
+ return !s(e);
93
+ }
94
+ var l = class extends Error {
95
+ constructor(e, t) {
96
+ super(e, t), this.message = e;
97
+ }
98
+ name = `GetPortError`;
99
+ };
100
+ function u(e, t) {
101
+ e && console.log(`[get-port] ${t}`);
102
+ }
103
+ function d(e, t) {
104
+ if (t < e) return [];
105
+ let n = [];
106
+ for (let r = e; r <= t; r++) n.push(r);
107
+ return n;
108
+ }
109
+ function f(e, n) {
110
+ return new Promise((r) => {
111
+ let i = createServer();
112
+ i.unref(), i.on(`error`, () => {
113
+ r(!1);
114
+ }), i.listen({
115
+ port: e,
116
+ host: n
117
+ }, () => {
118
+ let { port: e } = i.address();
119
+ i.close(() => {
120
+ r(c(e) && e);
121
+ });
122
+ });
123
+ });
124
+ }
125
+ function p(e) {
126
+ let t = new Set(e);
127
+ for (let e of Object.values(networkInterfaces())) for (let n of e || []) n.address && !n.internal && !n.address.startsWith(`fe80::`) && !n.address.startsWith(`169.254`) && t.add(n.address);
128
+ return [...t];
129
+ }
130
+ async function m(e, t) {
131
+ for (let n of e) {
132
+ let e = await f(n, t);
133
+ if (e) return e;
134
+ }
135
+ }
136
+ function h(e) {
137
+ return e ? `on host ${JSON.stringify(e)}` : `on any host`;
138
+ }
139
+ const g = /^(?!-)[\d.:A-Za-z-]{1,63}(?<!-)$/;
140
+ function _(e, t, n) {
141
+ if (e && !g.test(e)) {
142
+ let r = t ? `0.0.0.0` : `127.0.0.1`;
143
+ return u(n, `Invalid hostname: ${JSON.stringify(e)}. Using ${JSON.stringify(r)} as fallback.`), r;
144
+ }
145
+ return e;
146
+ }
147
+ async function v(e = {}) {
148
+ (typeof e == `number` || typeof e == `string`) && (e = { port: Number.parseInt(e + ``) || 0 });
149
+ let t = Number(e.port ?? process.env.PORT), n = !!(e.port || e.ports?.length || e.portRange?.length), r = {
150
+ random: t === 0,
151
+ ports: [],
152
+ portRange: [],
153
+ alternativePortRange: n ? [] : [3e3, 3100],
154
+ verbose: !1,
155
+ ...e,
156
+ port: t,
157
+ host: _(e.host ?? process.env.HOST, e.public, e.verbose)
158
+ };
159
+ if (r.random && !n) return y(r.host);
160
+ let i = [
161
+ r.port,
162
+ ...r.ports,
163
+ ...d(...r.portRange)
164
+ ].filter((e) => e ? c(e) ? !0 : (u(r.verbose, `Ignoring unsafe port: ${e}`), !1) : !1);
165
+ i.length === 0 && i.push(3e3);
166
+ let a = await m(i, r.host);
167
+ if (!a && r.alternativePortRange.length > 0 && (a = await m(d(...r.alternativePortRange), r.host), i.length > 0)) {
168
+ let e = `Unable to find an available port (tried ${i.join(`-`)} ${h(r.host)}).`;
169
+ a && (e += ` Using alternative port ${a}.`), u(r.verbose, e);
170
+ }
171
+ if (!a && e.random !== !1 && (a = await y(r.host), a && u(r.verbose, `Using random port ${a}`)), !a) {
172
+ let e = [
173
+ r.port,
174
+ r.portRange.join(`-`),
175
+ r.alternativePortRange.join(`-`)
176
+ ].filter(Boolean).join(`, `);
177
+ throw new l(`Unable to find an available port ${h(r.host)} (tried ${e})`);
178
+ }
179
+ return a;
180
+ }
181
+ async function y(e) {
182
+ let t = await x(0, e);
183
+ if (t === !1) throw new l(`Unable to find a random port ${h(e)}`);
184
+ return t;
185
+ }
186
+ async function x(e, t = process.env.HOST, n) {
187
+ if (t ||= p([void 0, `0.0.0.0`]), !Array.isArray(t)) return f(e, t);
188
+ for (let r of t) {
189
+ let t = await f(e, r);
190
+ if (t === !1) return e < 1024 && n && u(n, `Unable to listen to the privileged port ${e} ${h(r)}`), !1;
191
+ e === 0 && t !== 0 && (e = t);
192
+ }
193
+ return e;
194
+ }
195
+ //#endregion
196
+ export { v };
@@ -0,0 +1,44 @@
1
+ import n from "node:fs";
2
+ import t from "node:os";
3
+ import e from "node:process";
4
+ //#region inline/is-wsl/index.mjs
5
+ let r;
6
+ function i() {
7
+ try {
8
+ return n.statSync(`/.dockerenv`), !0;
9
+ } catch {
10
+ return !1;
11
+ }
12
+ }
13
+ function a() {
14
+ try {
15
+ return n.readFileSync(`/proc/self/cgroup`, `utf8`).includes(`docker`);
16
+ } catch {
17
+ return !1;
18
+ }
19
+ }
20
+ function o() {
21
+ return r === void 0 && (r = i() || a()), r;
22
+ }
23
+ let s;
24
+ const c = () => {
25
+ try {
26
+ return n.statSync(`/run/.containerenv`), !0;
27
+ } catch {
28
+ return !1;
29
+ }
30
+ };
31
+ function l() {
32
+ return s === void 0 && (s = c() || o()), s;
33
+ }
34
+ const u = () => {
35
+ if (e.platform !== `linux`) return !1;
36
+ if (t.release().toLowerCase().includes(`microsoft`)) return !l();
37
+ try {
38
+ if (n.readFileSync(`/proc/version`, `utf8`).toLowerCase().includes(`microsoft`)) return !l();
39
+ } catch {}
40
+ return n.existsSync(`/proc/sys/fs/binfmt_misc/WSLInterop`) || n.existsSync(`/run/WSL`) ? !l() : !1;
41
+ };
42
+ var d = e.env.__IS_WSL_TEST__ ? u : u();
43
+ //#endregion
44
+ export { d };
@@ -0,0 +1,23 @@
1
+ var normalize_path_default = ((e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports))(((e, t) => {
2
+ /*!
3
+ * normalize-path <https://github.com/jonschlinkert/normalize-path>
4
+ *
5
+ * Copyright (c) 2014-2018, Jon Schlinkert.
6
+ * Released under the MIT License.
7
+ */
8
+ t.exports = function(e, t) {
9
+ if (typeof e != `string`) throw TypeError(`expected path to be a string`);
10
+ if (e === `\\` || e === `/`) return `/`;
11
+ var n = e.length;
12
+ if (n <= 1) return e;
13
+ var r = ``;
14
+ if (n > 4 && e[3] === `\\`) {
15
+ var i = e[2];
16
+ (i === `?` || i === `.`) && e.slice(0, 2) === `\\\\` && (e = e.slice(2), r = `//`);
17
+ }
18
+ var a = e.split(/[/\\]+/);
19
+ return t !== !1 && a[a.length - 1] === `` && a.pop(), r + a.join(`/`);
20
+ };
21
+ }))();
22
+ //#endregion
23
+ export { normalize_path_default as default };
@@ -0,0 +1,113 @@
1
+ import { createHash } from "node:crypto";
2
+ //#region inline/ohash/index.mjs
3
+ function t(e) {
4
+ return typeof e == `string` ? `'${e}'` : new n().serialize(e);
5
+ }
6
+ const n = function() {
7
+ class e {
8
+ #e = /* @__PURE__ */ new Map();
9
+ compare(e, t) {
10
+ let n = typeof e, r = typeof t;
11
+ return n === `string` && r === `string` ? e.localeCompare(t) : n === `number` && r === `number` ? e - t : String.prototype.localeCompare.call(this.serialize(e, !0), this.serialize(t, !0));
12
+ }
13
+ serialize(e, t) {
14
+ if (e === null) return `null`;
15
+ switch (typeof e) {
16
+ case `string`: return t ? e : `'${e}'`;
17
+ case `bigint`: return `${e}n`;
18
+ case `object`: return this.$object(e);
19
+ case `function`: return this.$function(e);
20
+ }
21
+ return String(e);
22
+ }
23
+ serializeObject(e) {
24
+ let t = Object.prototype.toString.call(e);
25
+ if (t !== `[object Object]`) return this.serializeBuiltInType(t.length < 10 ? `unknown:${t}` : t.slice(8, -1), e);
26
+ let n = e.constructor, r = n === Object || n === void 0 ? `` : n.name;
27
+ if (r !== `` && globalThis[r] === n) return this.serializeBuiltInType(r, e);
28
+ if (typeof e.toJSON == `function`) {
29
+ let t = e.toJSON();
30
+ return r + (typeof t == `object` && t ? this.$object(t) : `(${this.serialize(t)})`);
31
+ }
32
+ return this.serializeObjectEntries(r, Object.entries(e));
33
+ }
34
+ serializeBuiltInType(e, t) {
35
+ let n = this[`$` + e];
36
+ if (n) return n.call(this, t);
37
+ if (typeof t?.entries == `function`) return this.serializeObjectEntries(e, t.entries());
38
+ throw Error(`Cannot serialize ${e}`);
39
+ }
40
+ serializeObjectEntries(e, t) {
41
+ let n = Array.from(t).sort((e, t) => this.compare(e[0], t[0])), r = `${e}{`;
42
+ for (let e = 0; e < n.length; e++) {
43
+ let [t, i] = n[e];
44
+ r += `${this.serialize(t, !0)}:${this.serialize(i)}`, e < n.length - 1 && (r += `,`);
45
+ }
46
+ return r + `}`;
47
+ }
48
+ $object(e) {
49
+ let t = this.#e.get(e);
50
+ return t === void 0 && (this.#e.set(e, `#${this.#e.size}`), t = this.serializeObject(e), this.#e.set(e, t)), t;
51
+ }
52
+ $function(e) {
53
+ let t = Function.prototype.toString.call(e);
54
+ return t.slice(-15) === `[native code] }` ? `${e.name || ``}()[native]` : `${e.name}(${e.length})${t.replace(/\s*\n\s*/g, ``)}`;
55
+ }
56
+ $Array(e) {
57
+ let t = `[`;
58
+ for (let n = 0; n < e.length; n++) t += this.serialize(e[n]), n < e.length - 1 && (t += `,`);
59
+ return t + `]`;
60
+ }
61
+ $Date(e) {
62
+ try {
63
+ return `Date(${e.toISOString()})`;
64
+ } catch {
65
+ return `Date(null)`;
66
+ }
67
+ }
68
+ $ArrayBuffer(e) {
69
+ return `ArrayBuffer[${new Uint8Array(e).join(`,`)}]`;
70
+ }
71
+ $Set(e) {
72
+ return `Set${this.$Array(Array.from(e).sort((e, t) => this.compare(e, t)))}`;
73
+ }
74
+ $Map(e) {
75
+ return this.serializeObjectEntries(`Map`, e.entries());
76
+ }
77
+ }
78
+ for (let t of [
79
+ `Error`,
80
+ `RegExp`,
81
+ `URL`
82
+ ]) e.prototype[`$` + t] = function(e) {
83
+ return `${t}(${e})`;
84
+ };
85
+ for (let t of [
86
+ `Int8Array`,
87
+ `Uint8Array`,
88
+ `Uint8ClampedArray`,
89
+ `Int16Array`,
90
+ `Uint16Array`,
91
+ `Int32Array`,
92
+ `Uint32Array`,
93
+ `Float32Array`,
94
+ `Float64Array`
95
+ ]) e.prototype[`$` + t] = function(e) {
96
+ return `${t}[${e.join(`,`)}]`;
97
+ };
98
+ for (let t of [`BigInt64Array`, `BigUint64Array`]) e.prototype[`$` + t] = function(e) {
99
+ return `${t}[${e.join(`n,`)}${e.length > 0 ? `n` : ``}]`;
100
+ };
101
+ return e;
102
+ }();
103
+ const i = globalThis.process?.getBuiltinModule?.(`crypto`)?.hash, a = `sha256`, o = `base64url`;
104
+ function s(t) {
105
+ if (i) return i(a, t, o);
106
+ let n = createHash(a).update(t);
107
+ return globalThis.process?.versions?.webcontainer ? n.digest().toString(o) : n.digest(o);
108
+ }
109
+ function c(e) {
110
+ return s(t(e));
111
+ }
112
+ //#endregion
113
+ export { c };
@@ -0,0 +1,50 @@
1
+ //#region inline/scule/index.mjs
2
+ const e = /\d/, t = [
3
+ `-`,
4
+ `_`,
5
+ `/`,
6
+ `.`
7
+ ];
8
+ function n(t = ``) {
9
+ if (!e.test(t)) return t !== t.toLowerCase();
10
+ }
11
+ function r(e, r) {
12
+ let i = r ?? t, a = [];
13
+ if (!e || typeof e != `string`) return a;
14
+ let o = ``, s, c;
15
+ for (let t of e) {
16
+ let e = i.includes(t);
17
+ if (e === !0) {
18
+ a.push(o), o = ``, s = void 0;
19
+ continue;
20
+ }
21
+ let r = n(t);
22
+ if (c === !1) {
23
+ if (s === !1 && r === !0) {
24
+ a.push(o), o = t, s = r;
25
+ continue;
26
+ }
27
+ if (s === !0 && r === !1 && o.length > 1) {
28
+ let e = o.at(-1);
29
+ a.push(o.slice(0, Math.max(0, o.length - 1))), o = e + t, s = r;
30
+ continue;
31
+ }
32
+ }
33
+ o += t, s = r, c = e;
34
+ }
35
+ return a.push(o), a;
36
+ }
37
+ function i(e) {
38
+ return e ? e[0].toUpperCase() + e.slice(1) : ``;
39
+ }
40
+ function a(e) {
41
+ return e ? e[0].toLowerCase() + e.slice(1) : ``;
42
+ }
43
+ function o(e, t) {
44
+ return e ? (Array.isArray(e) ? e : r(e)).map((e) => i(t?.normalize ? e.toLowerCase() : e)).join(``) : ``;
45
+ }
46
+ function s(e, t) {
47
+ return a(o(e || ``, t));
48
+ }
49
+ //#endregion
50
+ export { s };
package/dist/types.d.mts CHANGED
@@ -634,6 +634,17 @@ interface Logger {
634
634
  success(...args: any[]): void;
635
635
  level: LogLevel;
636
636
  }
637
+ /**
638
+ * The logger available at `wxt.logger`. Extends {@link Logger} with a `warnOnce`
639
+ * which only logs a message once per process.
640
+ */
641
+ interface WxtLogger extends Logger {
642
+ /**
643
+ * Same as {@link Logger.warn}, but only logs a given message once per process,
644
+ * even if called multiple times with the same arguments.
645
+ */
646
+ warnOnce(...args: any[]): void;
647
+ }
637
648
  interface BaseEntrypointOptions {
638
649
  /**
639
650
  * List of target browsers to include this entrypoint in. Defaults to being
@@ -1122,21 +1133,6 @@ interface WebExtConfig {
1122
1133
  * default_directory: "/my/custom/dir",
1123
1134
  * },
1124
1135
  * }
1125
- *
1126
- * @default
1127
- * // Enable dev mode and allow content script sourcemaps
1128
- * {
1129
- * devtools: {
1130
- * synced_preferences_sync_disabled: {
1131
- * skipContentScripts: false,
1132
- * },
1133
- * }
1134
- * extensions: {
1135
- * ui: {
1136
- * developer_mode: true,
1137
- * },
1138
- * }
1139
- * }
1140
1136
  */
1141
1137
  chromiumPref?: Record<string, any>;
1142
1138
  /**
@@ -1381,8 +1377,8 @@ interface Wxt {
1381
1377
  hooks: Hookable<WxtHooks>;
1382
1378
  /** Alias for `wxt.hooks.hook(...)`. */
1383
1379
  hook: Hookable<WxtHooks>['hook'];
1384
- /** Alias for config.logger */
1385
- logger: Logger;
1380
+ /** Wraps `config.logger`, adding `warnOnce`. */
1381
+ logger: WxtLogger;
1386
1382
  /** Reload config file and update `wxt.config` with the result. */
1387
1383
  reloadConfig: () => Promise<void>;
1388
1384
  /** Package manager utilities. */
@@ -1433,7 +1429,7 @@ interface ResolvedConfig$1 {
1433
1429
  targetBrowsers: TargetBrowser[];
1434
1430
  manifestVersion: TargetManifestVersion;
1435
1431
  env: ConfigEnv;
1436
- logger: Logger;
1432
+ logger: WxtLogger;
1437
1433
  imports: WxtResolvedUnimportOptions;
1438
1434
  manifest: UserManifest;
1439
1435
  fsCache: FsCache;
@@ -1526,28 +1522,29 @@ interface ExtensionRunner {
1526
1522
  canOpen?(): boolean;
1527
1523
  }
1528
1524
  type EslintGlobalsPropValue = boolean | 'readonly' | 'readable' | 'writable' | 'writeable';
1525
+ type EslintConfigVersion = 8 | 9;
1529
1526
  interface Eslintrc {
1530
1527
  /**
1531
- * When true, generates a file that can be used by ESLint to know which
1532
- * variables are valid globals.
1528
+ * Determines if and in what format a config file will be generated to inform
1529
+ * ESLint of unimport globals.
1533
1530
  *
1534
- * - `'auto'`: Check if eslint is installed, and if it is, generate a compatible
1535
- * config file.
1536
- * - `true`: Same as `'auto'`.
1537
- * - `false`: Don't generate the file.
1538
- * - `8`: Generate a config file compatible with ESLint 8.
1539
- * - `9`: Generate a config file compatible with ESLint 9.
1531
+ * - `true`: If eslint is installed, generate a compatible config file based on
1532
+ * the installed version.
1533
+ * - `false`: Never generate the file.
1534
+ * - `8`: Generate an eslintrc file compatible with ESLint &lte; 8.
1535
+ * - `9`: Generate a flat config file compatible with ESLint &gte; 9.
1536
+ * - `'auto'` (Deprecated): Same as `true`.
1540
1537
  *
1541
- * @default 'auto'
1538
+ * @default true
1542
1539
  */
1543
- enabled?: 'auto' | boolean | 8 | 9;
1540
+ enabled?: boolean | 'auto' | EslintConfigVersion;
1544
1541
  /**
1545
1542
  * File path to save the generated eslint config.
1546
1543
  *
1547
1544
  * Default depends on version of ESLint used:
1548
1545
  *
1549
- * - 9 and above: './.wxt/eslint-auto-imports.mjs'
1550
- * - 8 and below: './.wxt/eslintrc-auto-import.json'
1546
+ * - &gte; 9: './.wxt/eslint-auto-imports.mjs'
1547
+ * - &lte; 8: './.wxt/eslintrc-auto-import.json'
1551
1548
  */
1552
1549
  filePath?: string;
1553
1550
  /** @default true */
@@ -1555,7 +1552,7 @@ interface Eslintrc {
1555
1552
  }
1556
1553
  interface ResolvedEslintrc {
1557
1554
  /** False if disabled, otherwise the major version of ESLint installed */
1558
- enabled: false | 8 | 9;
1555
+ enabled: false | EslintConfigVersion;
1559
1556
  /** Absolute path */
1560
1557
  filePath: string;
1561
1558
  globalsPropValue: EslintGlobalsPropValue;
@@ -1701,4 +1698,4 @@ interface WxtDirFileEntry {
1701
1698
  tsReference?: boolean;
1702
1699
  }
1703
1700
  //#endregion
1704
- export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PrepareTsconfigs, 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 };
1701
+ export { BackgroundDefinition, BackgroundEntrypoint, BackgroundEntrypointOptions, BaseContentScriptEntrypointOptions, BaseEntrypoint, BaseEntrypointOptions, BaseScriptEntrypointOptions, BuildOutput, BuildStepOutput, ConfigEnv, ContentScriptDefinition, ContentScriptEntrypoint, CopiedPublicFile, Dependency, Entrypoint, EntrypointGroup, EntrypointInfo, EslintConfigVersion, EslintGlobalsPropValue, Eslintrc, ExtensionRunner, FirefoxDataCollectionPermissions, FirefoxDataCollectionType, FsCache, GeneratedPublicFile, GenericEntrypoint, HookResult, InlineConfig, IsolatedWorldContentScriptDefinition, IsolatedWorldContentScriptEntrypointOptions, Logger, MainWorldContentScriptDefinition, MainWorldContentScriptEntrypointOptions, OnContentScriptStopped, OptionsEntrypoint, OptionsEntrypointOptions, OutputAsset, OutputChunk, OutputFile, PerBrowserMap, PerBrowserOption, PopupEntrypoint, PopupEntrypointOptions, PrepareTsconfigs, 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, WxtLogger, WxtModule, WxtModuleOptions, WxtModuleSetup, WxtModuleWithMetadata, WxtPackageManager, WxtPlugin, WxtResolvedUnimportOptions, WxtUnimportOptions, WxtViteConfig };
@@ -6,6 +6,8 @@ import * as _$wxt_browser0 from "wxt/browser";
6
6
  /**
7
7
  * Create a content script UI using an iframe.
8
8
  *
9
+ * @param options - Iframe options. See {@link ContentScriptUiOptions} for shared
10
+ * positioning, anchoring, `append`, and removal options.
9
11
  * @see https://wxt.dev/guide/essentials/content-scripts.html#iframe
10
12
  */
11
13
  declare function createIframeUi<TMounted>(ctx: ContentScriptContext, options: IframeContentScriptUiOptions<TMounted>): IframeContentScriptUi<TMounted>;
@@ -5,6 +5,8 @@ import { browser } from "wxt/browser";
5
5
  /**
6
6
  * Create a content script UI using an iframe.
7
7
  *
8
+ * @param options - Iframe options. See {@link ContentScriptUiOptions} for shared
9
+ * positioning, anchoring, `append`, and removal options.
8
10
  * @see https://wxt.dev/guide/essentials/content-scripts.html#iframe
9
11
  */
10
12
  function createIframeUi(ctx, options) {
@@ -5,6 +5,8 @@ import { ContentScriptUi, ContentScriptUiOptions } from "./types.mjs";
5
5
  /**
6
6
  * Create a content script UI without any isolation.
7
7
  *
8
+ * @param options - Integrated UI options. See {@link ContentScriptUiOptions} for
9
+ * shared positioning, anchoring, `append`, and removal options.
8
10
  * @see https://wxt.dev/guide/essentials/content-scripts.html#integrated
9
11
  */
10
12
  declare function createIntegratedUi<TMounted>(ctx: ContentScriptContext, options: IntegratedContentScriptUiOptions<TMounted>): IntegratedContentScriptUi<TMounted>;
@@ -3,6 +3,8 @@ import { applyPosition, createMountFunctions, mountUi } from "./shared.mjs";
3
3
  /**
4
4
  * Create a content script UI without any isolation.
5
5
  *
6
+ * @param options - Integrated UI options. See {@link ContentScriptUiOptions} for
7
+ * shared positioning, anchoring, `append`, and removal options.
6
8
  * @see https://wxt.dev/guide/essentials/content-scripts.html#integrated
7
9
  */
8
10
  function createIntegratedUi(ctx, options) {
@@ -8,6 +8,8 @@ import { ContentScriptUi, ContentScriptUiOptions } from "./types.mjs";
8
8
  *
9
9
  * > This function is async because it has to load the CSS via a network call.
10
10
  *
11
+ * @param options - Shadow root options. See {@link ContentScriptUiOptions} for
12
+ * shared positioning, anchoring, `append`, and removal options.
11
13
  * @see https://wxt.dev/guide/essentials/content-scripts.html#shadow-root
12
14
  */
13
15
  declare function createShadowRootUi<TMounted>(ctx: ContentScriptContext, options: ShadowRootContentScriptUiOptions<TMounted>): Promise<ShadowRootContentScriptUi<TMounted>>;
@@ -11,6 +11,8 @@ import { createIsolatedElement } from "@webext-core/isolated-element";
11
11
  *
12
12
  * > This function is async because it has to load the CSS via a network call.
13
13
  *
14
+ * @param options - Shadow root options. See {@link ContentScriptUiOptions} for
15
+ * shared positioning, anchoring, `append`, and removal options.
14
16
  * @see https://wxt.dev/guide/essentials/content-scripts.html#shadow-root
15
17
  */
16
18
  async function createShadowRootUi(ctx, options) {
package/dist/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region src/version.ts
2
- const version = "0.21.3";
2
+ const version = "0.21.4";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wxt",
3
3
  "type": "module",
4
- "version": "0.21.3",
4
+ "version": "0.21.4",
5
5
  "description": "⚡ Next-gen Web Extension Framework",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -22,7 +22,7 @@
22
22
  "@webext-core/fake-browser": "^2.0.1",
23
23
  "@webext-core/isolated-element": "^1.1.3 || ^2 || ^3",
24
24
  "@webext-core/match-patterns": "^2.0.0",
25
- "@wxt-dev/browser": "^0.2.2",
25
+ "@wxt-dev/browser": ">=0.1",
26
26
  "@wxt-dev/storage": "^1.0.0",
27
27
  "c12": "^3.3.4",
28
28
  "cac": "^6.7.14 || ^7.0.0",
@@ -31,23 +31,17 @@
31
31
  "defu": "^6.1.4",
32
32
  "dotenv-expand": "^13.0.0",
33
33
  "filesize": "^11.0.17",
34
- "get-port-please": "^3.2.0",
35
34
  "giget": "^1.2.3 || ^2.0.0 || ^3.0.0",
36
35
  "hookable": "^6.1.0",
37
- "is-wsl": "^3.1.1",
38
36
  "json5": "^2.2.3",
39
37
  "linkedom": "^0.18.12",
40
38
  "magicast": "^0.5.2",
41
- "nano-spawn": "^2.0.0",
42
- "nanospinner": "^1.2.2",
43
- "normalize-path": "^3.0.0",
44
39
  "nypm": "^0.6.5",
45
- "ohash": "^2.0.11",
46
40
  "picomatch": "^4.0.3",
47
41
  "publish-browser-extension": "^5.1.0 || ^6.0.0",
48
- "scule": "^1.3.0",
49
42
  "superlock": "^1.3.2",
50
43
  "tiny-open": "^1.3.0",
44
+ "tinyexec": "^1.2.4",
51
45
  "tinyglobby": "^0.2.16",
52
46
  "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0 || ^6.0.0"
53
47
  },
@@ -79,10 +73,15 @@
79
73
  "@types/prompts": "^2.4.9",
80
74
  "eslint": "^10.1.0",
81
75
  "extract-zip": "^2.0.1",
76
+ "get-port-please": "^3.2.0",
82
77
  "happy-dom": "^20.8.3",
78
+ "is-wsl": "^3.1.1",
83
79
  "lodash.merge": "^4.6.2",
80
+ "normalize-path": "^3.0.0",
81
+ "ohash": "^2.0.11",
84
82
  "oxlint": "^1.63.0",
85
83
  "publint": "^0.3.18",
84
+ "scule": "^1.3.0",
86
85
  "tsdown": "^0.21.0",
87
86
  "typescript": "^6.0.3",
88
87
  "vite": "^8.2.0",