wxt 0.10.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -40,7 +40,7 @@ var require_windows = __commonJS({
40
40
  module2.exports = isexe;
41
41
  isexe.sync = sync;
42
42
  var fs16 = require("fs");
43
- function checkPathExt(path11, options) {
43
+ function checkPathExt(path10, options) {
44
44
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
45
45
  if (!pathext) {
46
46
  return true;
@@ -51,25 +51,25 @@ var require_windows = __commonJS({
51
51
  }
52
52
  for (var i = 0; i < pathext.length; i++) {
53
53
  var p = pathext[i].toLowerCase();
54
- if (p && path11.substr(-p.length).toLowerCase() === p) {
54
+ if (p && path10.substr(-p.length).toLowerCase() === p) {
55
55
  return true;
56
56
  }
57
57
  }
58
58
  return false;
59
59
  }
60
- function checkStat(stat, path11, options) {
60
+ function checkStat(stat, path10, options) {
61
61
  if (!stat.isSymbolicLink() && !stat.isFile()) {
62
62
  return false;
63
63
  }
64
- return checkPathExt(path11, options);
64
+ return checkPathExt(path10, options);
65
65
  }
66
- function isexe(path11, options, cb) {
67
- fs16.stat(path11, function(er, stat) {
68
- cb(er, er ? false : checkStat(stat, path11, options));
66
+ function isexe(path10, options, cb) {
67
+ fs16.stat(path10, function(er, stat) {
68
+ cb(er, er ? false : checkStat(stat, path10, options));
69
69
  });
70
70
  }
71
- function sync(path11, options) {
72
- return checkStat(fs16.statSync(path11), path11, options);
71
+ function sync(path10, options) {
72
+ return checkStat(fs16.statSync(path10), path10, options);
73
73
  }
74
74
  }
75
75
  });
@@ -81,13 +81,13 @@ var require_mode = __commonJS({
81
81
  module2.exports = isexe;
82
82
  isexe.sync = sync;
83
83
  var fs16 = require("fs");
84
- function isexe(path11, options, cb) {
85
- fs16.stat(path11, function(er, stat) {
84
+ function isexe(path10, options, cb) {
85
+ fs16.stat(path10, function(er, stat) {
86
86
  cb(er, er ? false : checkStat(stat, options));
87
87
  });
88
88
  }
89
- function sync(path11, options) {
90
- return checkStat(fs16.statSync(path11), options);
89
+ function sync(path10, options) {
90
+ return checkStat(fs16.statSync(path10), options);
91
91
  }
92
92
  function checkStat(stat, options) {
93
93
  return stat.isFile() && checkMode(stat, options);
@@ -121,7 +121,7 @@ var require_isexe = __commonJS({
121
121
  }
122
122
  module2.exports = isexe;
123
123
  isexe.sync = sync;
124
- function isexe(path11, options, cb) {
124
+ function isexe(path10, options, cb) {
125
125
  if (typeof options === "function") {
126
126
  cb = options;
127
127
  options = {};
@@ -131,7 +131,7 @@ var require_isexe = __commonJS({
131
131
  throw new TypeError("callback not provided");
132
132
  }
133
133
  return new Promise(function(resolve14, reject) {
134
- isexe(path11, options || {}, function(er, is) {
134
+ isexe(path10, options || {}, function(er, is) {
135
135
  if (er) {
136
136
  reject(er);
137
137
  } else {
@@ -140,7 +140,7 @@ var require_isexe = __commonJS({
140
140
  });
141
141
  });
142
142
  }
143
- core(path11, options || {}, function(er, is) {
143
+ core(path10, options || {}, function(er, is) {
144
144
  if (er) {
145
145
  if (er.code === "EACCES" || options && options.ignoreErrors) {
146
146
  er = null;
@@ -150,9 +150,9 @@ var require_isexe = __commonJS({
150
150
  cb(er, is);
151
151
  });
152
152
  }
153
- function sync(path11, options) {
153
+ function sync(path10, options) {
154
154
  try {
155
- return core.sync(path11, options || {});
155
+ return core.sync(path10, options || {});
156
156
  } catch (er) {
157
157
  if (options && options.ignoreErrors || er.code === "EACCES") {
158
158
  return false;
@@ -169,7 +169,7 @@ var require_which = __commonJS({
169
169
  "node_modules/.pnpm/which@2.0.2/node_modules/which/which.js"(exports, module2) {
170
170
  "use strict";
171
171
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
172
- var path11 = require("path");
172
+ var path10 = require("path");
173
173
  var COLON = isWindows ? ";" : ":";
174
174
  var isexe = require_isexe();
175
175
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -207,7 +207,7 @@ var require_which = __commonJS({
207
207
  return opt.all && found.length ? resolve14(found) : reject(getNotFoundError(cmd));
208
208
  const ppRaw = pathEnv[i];
209
209
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
210
- const pCmd = path11.join(pathPart, cmd);
210
+ const pCmd = path10.join(pathPart, cmd);
211
211
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
212
212
  resolve14(subStep(p, i, 0));
213
213
  });
@@ -234,7 +234,7 @@ var require_which = __commonJS({
234
234
  for (let i = 0; i < pathEnv.length; i++) {
235
235
  const ppRaw = pathEnv[i];
236
236
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
237
- const pCmd = path11.join(pathPart, cmd);
237
+ const pCmd = path10.join(pathPart, cmd);
238
238
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
239
239
  for (let j = 0; j < pathExt.length; j++) {
240
240
  const cur = p + pathExt[j];
@@ -282,7 +282,7 @@ var require_path_key = __commonJS({
282
282
  var require_resolveCommand = __commonJS({
283
283
  "node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module2) {
284
284
  "use strict";
285
- var path11 = require("path");
285
+ var path10 = require("path");
286
286
  var which = require_which();
287
287
  var getPathKey = require_path_key();
288
288
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -300,7 +300,7 @@ var require_resolveCommand = __commonJS({
300
300
  try {
301
301
  resolved = which.sync(parsed.command, {
302
302
  path: env[getPathKey({ env })],
303
- pathExt: withoutPathExt ? path11.delimiter : void 0
303
+ pathExt: withoutPathExt ? path10.delimiter : void 0
304
304
  });
305
305
  } catch (e) {
306
306
  } finally {
@@ -309,7 +309,7 @@ var require_resolveCommand = __commonJS({
309
309
  }
310
310
  }
311
311
  if (resolved) {
312
- resolved = path11.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
312
+ resolved = path10.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
313
313
  }
314
314
  return resolved;
315
315
  }
@@ -363,8 +363,8 @@ var require_shebang_command = __commonJS({
363
363
  if (!match) {
364
364
  return null;
365
365
  }
366
- const [path11, argument] = match[0].replace(/#! ?/, "").split(" ");
367
- const binary = path11.split("/").pop();
366
+ const [path10, argument] = match[0].replace(/#! ?/, "").split(" ");
367
+ const binary = path10.split("/").pop();
368
368
  if (binary === "env") {
369
369
  return argument;
370
370
  }
@@ -399,7 +399,7 @@ var require_readShebang = __commonJS({
399
399
  var require_parse = __commonJS({
400
400
  "node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/parse.js"(exports, module2) {
401
401
  "use strict";
402
- var path11 = require("path");
402
+ var path10 = require("path");
403
403
  var resolveCommand = require_resolveCommand();
404
404
  var escape = require_escape();
405
405
  var readShebang = require_readShebang();
@@ -424,7 +424,7 @@ var require_parse = __commonJS({
424
424
  const needsShell = !isExecutableRegExp.test(commandFile);
425
425
  if (parsed.options.forceShell || needsShell) {
426
426
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
427
- parsed.command = path11.normalize(parsed.command);
427
+ parsed.command = path10.normalize(parsed.command);
428
428
  parsed.command = escape.command(parsed.command);
429
429
  parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
430
430
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -590,9 +590,9 @@ function npmRunPath(options = {}) {
590
590
  }
591
591
  function npmRunPathEnv({ env = import_node_process.default.env, ...options } = {}) {
592
592
  env = { ...env };
593
- const path11 = pathKey({ env });
594
- options.path = env[path11];
595
- env[path11] = npmRunPath(options);
593
+ const path10 = pathKey({ env });
594
+ options.path = env[path10];
595
+ env[path10] = npmRunPath(options);
596
596
  return env;
597
597
  }
598
598
  var import_node_process, import_node_path10, import_node_url;
@@ -2001,8 +2001,8 @@ var nativePromisePrototype, descriptors, mergePromise, getSpawnedPromise;
2001
2001
  var init_promise = __esm({
2002
2002
  "node_modules/.pnpm/execa@8.0.1/node_modules/execa/lib/promise.js"() {
2003
2003
  "use strict";
2004
- nativePromisePrototype = (/* @__PURE__ */ (async () => {
2005
- })()).constructor.prototype;
2004
+ nativePromisePrototype = (async () => {
2005
+ })().constructor.prototype;
2006
2006
  descriptors = ["then", "catch", "finally"].map((property) => [
2007
2007
  property,
2008
2008
  Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property)
@@ -2415,9 +2415,9 @@ var init_execa = __esm({
2415
2415
  // src/index.ts
2416
2416
  var src_exports = {};
2417
2417
  __export(src_exports, {
2418
- build: () => build2,
2418
+ build: () => build,
2419
2419
  clean: () => clean,
2420
- createServer: () => createServer2,
2420
+ createServer: () => createServer,
2421
2421
  defineConfig: () => defineConfig,
2422
2422
  defineRunnerConfig: () => defineRunnerConfig,
2423
2423
  initialize: () => initialize,
@@ -2427,25 +2427,171 @@ __export(src_exports, {
2427
2427
  });
2428
2428
  module.exports = __toCommonJS(src_exports);
2429
2429
 
2430
- // src/core/utils/building/build-entrypoints.ts
2431
- var vite = __toESM(require("vite"), 1);
2432
-
2433
- // src/core/utils/entrypoints.ts
2434
- var import_node_path2 = __toESM(require("path"), 1);
2430
+ // src/core/utils/fs.ts
2431
+ var import_fs_extra = __toESM(require("fs-extra"), 1);
2432
+ var import_fast_glob = __toESM(require("fast-glob"), 1);
2435
2433
 
2436
2434
  // src/core/utils/paths.ts
2437
2435
  var import_node_path = __toESM(require("path"), 1);
2438
2436
  var import_normalize_path = __toESM(require("normalize-path"), 1);
2439
- function normalizePath(path11) {
2440
- return (0, import_normalize_path.default)(path11);
2437
+ function normalizePath(path10) {
2438
+ return (0, import_normalize_path.default)(path10);
2441
2439
  }
2442
- function unnormalizePath(path11) {
2443
- return import_node_path.default.normalize(path11);
2440
+ function unnormalizePath(path10) {
2441
+ return import_node_path.default.normalize(path10);
2444
2442
  }
2445
2443
  var CSS_EXTENSIONS = ["css", "scss", "sass", "less", "styl", "stylus"];
2446
2444
  var CSS_EXTENSIONS_PATTERN = `+(${CSS_EXTENSIONS.join("|")})`;
2447
2445
 
2446
+ // src/core/utils/fs.ts
2447
+ async function writeFileIfDifferent(file, newContents) {
2448
+ const existingContents = await import_fs_extra.default.readFile(file, "utf-8").catch(() => void 0);
2449
+ if (existingContents !== newContents) {
2450
+ await import_fs_extra.default.writeFile(file, newContents);
2451
+ }
2452
+ }
2453
+ async function getPublicFiles(config) {
2454
+ if (!await import_fs_extra.default.exists(config.publicDir))
2455
+ return [];
2456
+ const files = await (0, import_fast_glob.default)("**/*", { cwd: config.publicDir });
2457
+ return files.map(unnormalizePath);
2458
+ }
2459
+
2460
+ // src/core/utils/building/build-entrypoints.ts
2461
+ var import_fs_extra2 = __toESM(require("fs-extra"), 1);
2462
+ var import_path = require("path");
2463
+ var import_picocolors = __toESM(require("picocolors"), 1);
2464
+ async function buildEntrypoints(groups, config, spinner) {
2465
+ const steps = [];
2466
+ for (let i = 0; i < groups.length; i++) {
2467
+ const group = groups[i];
2468
+ const groupNames = [group].flat().map((e) => e.name).join(import_picocolors.default.dim(", "));
2469
+ spinner.text = import_picocolors.default.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNames}`;
2470
+ steps.push(await config.builder.build(group));
2471
+ }
2472
+ const publicAssets = await copyPublicDirectory(config);
2473
+ return { publicAssets, steps };
2474
+ }
2475
+ async function copyPublicDirectory(config) {
2476
+ const files = await getPublicFiles(config);
2477
+ if (files.length === 0)
2478
+ return [];
2479
+ const publicAssets = [];
2480
+ for (const file of files) {
2481
+ const srcPath = (0, import_path.resolve)(config.publicDir, file);
2482
+ const outPath = (0, import_path.resolve)(config.outDir, file);
2483
+ await import_fs_extra2.default.ensureDir((0, import_path.dirname)(outPath));
2484
+ await import_fs_extra2.default.copyFile(srcPath, outPath);
2485
+ publicAssets.push({
2486
+ type: "asset",
2487
+ fileName: file
2488
+ });
2489
+ }
2490
+ return publicAssets;
2491
+ }
2492
+
2493
+ // src/core/utils/arrays.ts
2494
+ function every(array, predicate) {
2495
+ for (let i = 0; i < array.length; i++)
2496
+ if (!predicate(array[i], i))
2497
+ return false;
2498
+ return true;
2499
+ }
2500
+
2501
+ // src/core/utils/building/detect-dev-changes.ts
2502
+ function detectDevChanges(changedFiles, currentOutput) {
2503
+ if (currentOutput == null)
2504
+ return { type: "no-change" };
2505
+ const changedSteps = new Set(
2506
+ changedFiles.flatMap(
2507
+ (changedFile) => findEffectedSteps(changedFile, currentOutput)
2508
+ )
2509
+ );
2510
+ if (changedSteps.size === 0)
2511
+ return { type: "no-change" };
2512
+ const unchangedOutput = {
2513
+ manifest: currentOutput.manifest,
2514
+ steps: [],
2515
+ publicAssets: []
2516
+ };
2517
+ const changedOutput = {
2518
+ manifest: currentOutput.manifest,
2519
+ steps: [],
2520
+ publicAssets: []
2521
+ };
2522
+ for (const step of currentOutput.steps) {
2523
+ if (changedSteps.has(step)) {
2524
+ changedOutput.steps.push(step);
2525
+ } else {
2526
+ unchangedOutput.steps.push(step);
2527
+ }
2528
+ }
2529
+ for (const asset of currentOutput.publicAssets) {
2530
+ if (changedSteps.has(asset)) {
2531
+ changedOutput.publicAssets.push(asset);
2532
+ } else {
2533
+ unchangedOutput.publicAssets.push(asset);
2534
+ }
2535
+ }
2536
+ const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, ([_, file]) => file.endsWith(".html"));
2537
+ if (isOnlyHtmlChanges) {
2538
+ return {
2539
+ type: "html-reload",
2540
+ cachedOutput: unchangedOutput,
2541
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
2542
+ };
2543
+ }
2544
+ const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
2545
+ changedOutput.steps.flatMap((step) => step.entrypoints),
2546
+ (entry) => entry.type === "content-script"
2547
+ );
2548
+ if (isOnlyContentScripts) {
2549
+ return {
2550
+ type: "content-script-reload",
2551
+ cachedOutput: unchangedOutput,
2552
+ changedSteps: changedOutput.steps,
2553
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
2554
+ };
2555
+ }
2556
+ return {
2557
+ type: "extension-reload",
2558
+ cachedOutput: unchangedOutput,
2559
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
2560
+ };
2561
+ }
2562
+ function findEffectedSteps(changedFile, currentOutput) {
2563
+ const changes = [];
2564
+ const changedPath = normalizePath(changedFile[1]);
2565
+ const isChunkEffected = (chunk) => (
2566
+ // If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
2567
+ // fileName is normalized, relative bundle path
2568
+ chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
2569
+ // moduleIds are absolute, normalized paths
2570
+ chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
2571
+ );
2572
+ for (const step of currentOutput.steps) {
2573
+ const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
2574
+ if (effectedChunk)
2575
+ changes.push(step);
2576
+ }
2577
+ const effectedAsset = currentOutput.publicAssets.find(
2578
+ (chunk) => isChunkEffected(chunk)
2579
+ );
2580
+ if (effectedAsset)
2581
+ changes.push(effectedAsset);
2582
+ return changes;
2583
+ }
2584
+
2585
+ // src/core/utils/building/find-entrypoints.ts
2586
+ var import_path2 = require("path");
2587
+ var import_fs_extra3 = __toESM(require("fs-extra"), 1);
2588
+ var import_minimatch = require("minimatch");
2589
+ var import_linkedom = require("linkedom");
2590
+ var import_json5 = __toESM(require("json5"), 1);
2591
+ var import_fast_glob2 = __toESM(require("fast-glob"), 1);
2592
+
2448
2593
  // src/core/utils/entrypoints.ts
2594
+ var import_node_path2 = __toESM(require("path"), 1);
2449
2595
  function getEntrypointName(entrypointsDir, inputPath) {
2450
2596
  const relativePath = import_node_path2.default.relative(entrypointsDir, inputPath);
2451
2597
  const name = relativePath.split(/[\.\/\\]/, 2)[0];
@@ -2465,423 +2611,332 @@ function resolvePerBrowserOption(option, browser) {
2465
2611
  return option;
2466
2612
  }
2467
2613
 
2468
- // src/core/vite-plugins/devHtmlPrerender.ts
2469
- var import_linkedom = require("linkedom");
2470
- var import_path = require("path");
2471
- var reactRefreshPreamble = "";
2472
- function devHtmlPrerender(config) {
2473
- const htmlReloadId = "@wxt/reload-html";
2474
- const resolvedHtmlReloadId = (0, import_path.resolve)(
2475
- config.root,
2476
- "node_modules/wxt/dist/virtual/reload-html.js"
2477
- );
2478
- const virtualReactRefreshId = "@wxt/virtual-react-refresh";
2479
- const resolvedVirtualReactRefreshId = "\0" + virtualReactRefreshId;
2480
- return [
2481
- {
2482
- apply: "build",
2483
- name: "wxt:dev-html-prerender",
2484
- config() {
2485
- return {
2486
- resolve: {
2487
- alias: {
2488
- [htmlReloadId]: resolvedHtmlReloadId
2489
- }
2490
- }
2491
- };
2492
- },
2493
- // Convert scripts like src="./main.tsx" -> src="http://localhost:3000/entrypoints/popup/main.tsx"
2494
- // before the paths are replaced with their bundled path
2495
- transform(code, id) {
2496
- const server = config.server;
2497
- if (config.command !== "serve" || server == null || !id.endsWith(".html"))
2498
- return;
2499
- const { document } = (0, import_linkedom.parseHTML)(code);
2500
- const pointToDevServer = (querySelector, attr) => {
2501
- document.querySelectorAll(querySelector).forEach((element) => {
2502
- const src = element.getAttribute(attr);
2503
- if (!src)
2504
- return;
2505
- if ((0, import_path.isAbsolute)(src)) {
2506
- element.setAttribute(attr, server.origin + src);
2507
- } else if (src.startsWith(".")) {
2508
- const abs = (0, import_path.resolve)((0, import_path.dirname)(id), src);
2509
- const pathname = (0, import_path.relative)(config.root, abs);
2510
- element.setAttribute(attr, `${server.origin}/${pathname}`);
2511
- }
2512
- });
2513
- };
2514
- pointToDevServer("script[type=module]", "src");
2515
- pointToDevServer("link[rel=stylesheet]", "href");
2516
- const reloader = document.createElement("script");
2517
- reloader.src = htmlReloadId;
2518
- reloader.type = "module";
2519
- document.head.appendChild(reloader);
2520
- const newHtml = document.toString();
2521
- config.logger.debug("transform " + id);
2522
- config.logger.debug("Old HTML:\n" + code);
2523
- config.logger.debug("New HTML:\n" + newHtml);
2524
- return newHtml;
2525
- },
2526
- // Pass the HTML through the dev server to add dev-mode specific code
2527
- async transformIndexHtml(html, ctx) {
2528
- const server = config.server;
2529
- if (config.command !== "serve" || server == null)
2530
- return;
2531
- const originalUrl = `${server.origin}${ctx.path}`;
2532
- const name = getEntrypointName(config.entrypointsDir, ctx.filename);
2533
- const url2 = `${server.origin}/${name}.html`;
2534
- const serverHtml = await server.transformIndexHtml(
2535
- url2,
2536
- html,
2537
- originalUrl
2538
- );
2539
- const { document } = (0, import_linkedom.parseHTML)(serverHtml);
2540
- const reactRefreshScript = Array.from(
2541
- document.querySelectorAll("script[type=module]")
2542
- ).find((script) => script.innerHTML.includes("@react-refresh"));
2543
- if (reactRefreshScript) {
2544
- reactRefreshPreamble = reactRefreshScript.innerHTML;
2545
- const virtualScript = document.createElement("script");
2546
- virtualScript.type = "module";
2547
- virtualScript.src = `${server.origin}/${virtualReactRefreshId}`;
2548
- reactRefreshScript.replaceWith(virtualScript);
2549
- }
2550
- const viteClientScript = document.querySelector(
2551
- "script[src='/@vite/client']"
2614
+ // src/core/utils/constants.ts
2615
+ var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
2616
+
2617
+ // src/core/utils/building/find-entrypoints.ts
2618
+ async function findEntrypoints(config) {
2619
+ const relativePaths = await (0, import_fast_glob2.default)("**/*", {
2620
+ cwd: config.entrypointsDir
2621
+ });
2622
+ relativePaths.sort();
2623
+ const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
2624
+ let hasBackground = false;
2625
+ const possibleEntrypoints = await Promise.all(
2626
+ relativePaths.map(async (relativePath) => {
2627
+ const path10 = (0, import_path2.resolve)(config.entrypointsDir, relativePath);
2628
+ const matchingGlob = pathGlobs.find(
2629
+ (glob5) => (0, import_minimatch.minimatch)(relativePath, glob5)
2630
+ );
2631
+ if (matchingGlob == null) {
2632
+ config.logger.warn(
2633
+ `${relativePath} does not match any known entrypoint. Known entrypoints:
2634
+ ${JSON.stringify(
2635
+ PATH_GLOB_TO_TYPE_MAP,
2636
+ null,
2637
+ 2
2638
+ )}`
2552
2639
  );
2553
- if (viteClientScript) {
2554
- viteClientScript.src = `${server.origin}${viteClientScript.src}`;
2555
- }
2556
- const newHtml = document.toString();
2557
- config.logger.debug("transformIndexHtml " + ctx.filename);
2558
- config.logger.debug("Old HTML:\n" + html);
2559
- config.logger.debug("New HTML:\n" + newHtml);
2560
- return newHtml;
2640
+ return;
2561
2641
  }
2562
- },
2563
- {
2564
- name: "wxt:virtualize-react-refresh",
2565
- apply: "serve",
2566
- resolveId(id) {
2567
- if (id === `/${virtualReactRefreshId}`) {
2568
- return resolvedVirtualReactRefreshId;
2569
- }
2570
- if (id.startsWith("/chunks/")) {
2571
- return "\0noop";
2572
- }
2573
- },
2574
- load(id) {
2575
- if (id === resolvedVirtualReactRefreshId) {
2576
- return reactRefreshPreamble;
2577
- }
2578
- if (id === "\0noop") {
2579
- return "";
2580
- }
2581
- }
2582
- }
2583
- ];
2584
- }
2585
-
2586
- // src/core/vite-plugins/devServerGlobals.ts
2587
- function devServerGlobals(internalConfig) {
2588
- return {
2589
- name: "wxt:dev-server-globals",
2590
- config() {
2591
- if (internalConfig.server == null || internalConfig.command == "build")
2642
+ const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
2643
+ if (type === "ignored")
2592
2644
  return;
2593
- return {
2594
- define: {
2595
- __DEV_SERVER_PROTOCOL__: JSON.stringify("ws:"),
2596
- __DEV_SERVER_HOSTNAME__: JSON.stringify(
2597
- internalConfig.server.hostname
2598
- ),
2599
- __DEV_SERVER_PORT__: JSON.stringify(internalConfig.server.port)
2600
- }
2601
- };
2645
+ switch (type) {
2646
+ case "popup":
2647
+ return await getPopupEntrypoint(config, path10);
2648
+ case "options":
2649
+ return await getOptionsEntrypoint(config, path10);
2650
+ case "background":
2651
+ hasBackground = true;
2652
+ return await getBackgroundEntrypoint(config, path10);
2653
+ case "content-script":
2654
+ return await getContentScriptEntrypoint(config, path10);
2655
+ case "unlisted-page":
2656
+ return await getUnlistedPageEntrypoint(config, path10);
2657
+ case "unlisted-script":
2658
+ return await getUnlistedScriptEntrypoint(config, path10);
2659
+ case "content-script-style":
2660
+ return {
2661
+ type,
2662
+ name: getEntrypointName(config.entrypointsDir, path10),
2663
+ inputPath: path10,
2664
+ outputDir: (0, import_path2.resolve)(config.outDir, CONTENT_SCRIPT_OUT_DIR),
2665
+ options: {
2666
+ include: void 0,
2667
+ exclude: void 0
2668
+ }
2669
+ };
2670
+ default:
2671
+ return {
2672
+ type,
2673
+ name: getEntrypointName(config.entrypointsDir, path10),
2674
+ inputPath: path10,
2675
+ outputDir: config.outDir,
2676
+ options: {
2677
+ include: void 0,
2678
+ exclude: void 0
2679
+ }
2680
+ };
2681
+ }
2682
+ })
2683
+ );
2684
+ const entrypoints = possibleEntrypoints.filter(
2685
+ (entry) => !!entry
2686
+ );
2687
+ const existingNames = {};
2688
+ entrypoints.forEach((entrypoint) => {
2689
+ const withSameName = existingNames[entrypoint.name];
2690
+ if (withSameName) {
2691
+ throw Error(
2692
+ `Multiple entrypoints with the name "${entrypoint.name}" detected, but only one is allowed: ${[
2693
+ (0, import_path2.relative)(config.root, withSameName.inputPath),
2694
+ (0, import_path2.relative)(config.root, entrypoint.inputPath)
2695
+ ].join(", ")}`
2696
+ );
2602
2697
  }
2603
- };
2604
- }
2605
-
2606
- // src/core/utils/network.ts
2607
- var import_node_dns = __toESM(require("dns"), 1);
2608
-
2609
- // src/core/utils/time.ts
2610
- function formatDuration(duration) {
2611
- if (duration < 1e3)
2612
- return `${duration} ms`;
2613
- if (duration < 1e4)
2614
- return `${(duration / 1e3).toFixed(3)} s`;
2615
- if (duration < 6e4)
2616
- return `${(duration / 1e3).toFixed(1)} s`;
2617
- return `${(duration / 1e3).toFixed(0)} s`;
2618
- }
2619
- function withTimeout(promise, duration) {
2620
- return new Promise((res, rej) => {
2621
- const timeout = setTimeout(() => {
2622
- rej(`Promise timed out after ${duration}ms`);
2623
- }, duration);
2624
- promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
2698
+ existingNames[entrypoint.name] = entrypoint;
2625
2699
  });
2626
- }
2627
-
2628
- // src/core/utils/network.ts
2629
- function isOffline() {
2630
- const isOffline2 = new Promise((res) => {
2631
- import_node_dns.default.resolve("google.com", (err) => {
2632
- if (err == null) {
2633
- res(false);
2634
- } else {
2635
- res(true);
2636
- }
2637
- });
2700
+ if (config.command === "serve" && !hasBackground) {
2701
+ entrypoints.push(
2702
+ await getBackgroundEntrypoint(config, VIRTUAL_NOOP_BACKGROUND_MODULE_ID)
2703
+ );
2704
+ }
2705
+ config.logger.debug("All entrypoints:", entrypoints);
2706
+ const targetEntrypoints = entrypoints.filter((entry) => {
2707
+ const { include, exclude } = entry.options;
2708
+ if (include?.length && exclude?.length) {
2709
+ config.logger.warn(
2710
+ `The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`
2711
+ );
2712
+ return false;
2713
+ }
2714
+ if (exclude?.length && !include?.length) {
2715
+ return !exclude.includes(config.browser);
2716
+ }
2717
+ if (include?.length && !exclude?.length) {
2718
+ return include.includes(config.browser);
2719
+ }
2720
+ return true;
2638
2721
  });
2639
- return withTimeout(isOffline2, 1e3).catch(() => true);
2722
+ config.logger.debug(`${config.browser} entrypoints:`, targetEntrypoints);
2723
+ return targetEntrypoints;
2640
2724
  }
2641
- async function isOnline() {
2642
- const offline = await isOffline();
2643
- return !offline;
2725
+ function getHtmlBaseOptions(document) {
2726
+ const options = {};
2727
+ const includeContent = document.querySelector("meta[name='manifest.include']")?.getAttribute("content");
2728
+ if (includeContent) {
2729
+ options.include = import_json5.default.parse(includeContent);
2730
+ }
2731
+ const excludeContent = document.querySelector("meta[name='manifest.exclude']")?.getAttribute("content");
2732
+ if (excludeContent) {
2733
+ options.exclude = import_json5.default.parse(excludeContent);
2734
+ }
2735
+ return options;
2644
2736
  }
2645
- async function fetchCached(url2, config) {
2646
- let content = "";
2647
- if (await isOnline()) {
2648
- const res = await fetch(url2);
2649
- if (res.status < 300) {
2650
- content = await res.text();
2651
- await config.fsCache.set(url2, content);
2652
- } else {
2653
- config.logger.debug(
2654
- `Failed to download "${url2}", falling back to cache...`
2737
+ async function getPopupEntrypoint(config, path10) {
2738
+ const content = await import_fs_extra3.default.readFile(path10, "utf-8");
2739
+ const { document } = (0, import_linkedom.parseHTML)(content);
2740
+ const options = getHtmlBaseOptions(document);
2741
+ const title = document.querySelector("title");
2742
+ if (title != null)
2743
+ options.defaultTitle = title.textContent ?? void 0;
2744
+ const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
2745
+ if (defaultIconContent) {
2746
+ try {
2747
+ options.defaultIcon = import_json5.default.parse(defaultIconContent);
2748
+ } catch (err) {
2749
+ config.logger.fatal(
2750
+ `Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
2751
+ err
2655
2752
  );
2656
2753
  }
2657
2754
  }
2658
- if (!content)
2659
- content = await config.fsCache.get(url2) ?? "";
2660
- if (!content)
2661
- throw Error(
2662
- `Offline and "${url2}" has not been cached. Try again when online.`
2663
- );
2664
- return content;
2755
+ const mv2TypeContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
2756
+ if (mv2TypeContent) {
2757
+ options.mv2Key = mv2TypeContent === "page_action" ? "page_action" : "browser_action";
2758
+ }
2759
+ const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
2760
+ if (browserStyleContent) {
2761
+ options.browserStyle = browserStyleContent === "true";
2762
+ }
2763
+ return {
2764
+ type: "popup",
2765
+ name: "popup",
2766
+ options,
2767
+ inputPath: path10,
2768
+ outputDir: config.outDir
2769
+ };
2665
2770
  }
2666
-
2667
- // src/core/vite-plugins/download.ts
2668
- function download(config) {
2771
+ async function getOptionsEntrypoint(config, path10) {
2772
+ const content = await import_fs_extra3.default.readFile(path10, "utf-8");
2773
+ const { document } = (0, import_linkedom.parseHTML)(content);
2774
+ const options = getHtmlBaseOptions(document);
2775
+ const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
2776
+ if (openInTabContent) {
2777
+ options.openInTab = openInTabContent === "true";
2778
+ }
2779
+ const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
2780
+ if (chromeStyleContent) {
2781
+ options.chromeStyle = chromeStyleContent === "true";
2782
+ }
2783
+ const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
2784
+ if (browserStyleContent) {
2785
+ options.browserStyle = browserStyleContent === "true";
2786
+ }
2669
2787
  return {
2670
- name: "wxt:download",
2671
- resolveId(id) {
2672
- if (id.startsWith("url:"))
2673
- return "\0" + id;
2674
- },
2675
- async load(id) {
2676
- if (!id.startsWith("\0url:"))
2677
- return;
2678
- const url2 = id.replace("\0url:", "");
2679
- return await fetchCached(url2, config);
2680
- }
2788
+ type: "options",
2789
+ name: "options",
2790
+ options,
2791
+ inputPath: path10,
2792
+ outputDir: config.outDir
2681
2793
  };
2682
2794
  }
2683
-
2684
- // src/core/vite-plugins/multipageMove.ts
2685
- var import_node_path3 = require("path");
2686
- var import_fs_extra = __toESM(require("fs-extra"), 1);
2687
- function multipageMove(entrypoints, config) {
2795
+ async function getUnlistedPageEntrypoint(config, path10) {
2796
+ const content = await import_fs_extra3.default.readFile(path10, "utf-8");
2797
+ const { document } = (0, import_linkedom.parseHTML)(content);
2688
2798
  return {
2689
- name: "wxt:multipage-move",
2690
- async writeBundle(_, bundle) {
2691
- for (const oldBundlePath in bundle) {
2692
- const entrypoint = entrypoints.find(
2693
- (entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath)
2694
- );
2695
- if (entrypoint == null) {
2696
- config.logger.debug(
2697
- `No entrypoint found for ${oldBundlePath}, leaving in chunks directory`
2698
- );
2699
- continue;
2700
- }
2701
- const newBundlePath = getEntrypointBundlePath(
2702
- entrypoint,
2703
- config.outDir,
2704
- (0, import_node_path3.extname)(oldBundlePath)
2705
- );
2706
- if (newBundlePath === oldBundlePath) {
2707
- config.logger.debug(
2708
- "HTML file is already in the correct location",
2709
- oldBundlePath
2710
- );
2711
- continue;
2712
- }
2713
- const oldAbsPath = (0, import_node_path3.resolve)(config.outDir, oldBundlePath);
2714
- const newAbsPath = (0, import_node_path3.resolve)(config.outDir, newBundlePath);
2715
- await (0, import_fs_extra.ensureDir)((0, import_node_path3.dirname)(newAbsPath));
2716
- await import_fs_extra.default.move(oldAbsPath, newAbsPath, { overwrite: true });
2717
- const renamedChunk = {
2718
- ...bundle[oldBundlePath],
2719
- fileName: newBundlePath
2720
- };
2721
- delete bundle[oldBundlePath];
2722
- bundle[newBundlePath] = renamedChunk;
2723
- }
2724
- }
2799
+ type: "unlisted-page",
2800
+ name: getEntrypointName(config.entrypointsDir, path10),
2801
+ inputPath: path10,
2802
+ outputDir: config.outDir,
2803
+ options: getHtmlBaseOptions(document)
2725
2804
  };
2726
2805
  }
2727
-
2728
- // src/core/vite-plugins/unimport.ts
2729
- var import_unimport = require("unimport");
2730
-
2731
- // src/core/utils/unimport.ts
2732
- var import_vite = require("vite");
2733
- function getUnimportOptions(config) {
2734
- if (config.imports === false)
2735
- return false;
2736
- const defaultOptions = {
2737
- debugLog: config.logger.debug,
2738
- imports: [
2739
- { name: "defineConfig", from: "wxt" },
2740
- { name: "fakeBrowser", from: "wxt/testing" }
2741
- ],
2742
- presets: [
2743
- { package: "wxt/client" },
2744
- { package: "wxt/browser" },
2745
- { package: "wxt/sandbox" },
2746
- { package: "wxt/storage" }
2747
- ],
2748
- warn: config.logger.warn,
2749
- dirs: ["components", "composables", "hooks", "utils"]
2750
- };
2751
- return (0, import_vite.mergeConfig)(
2752
- defaultOptions,
2753
- config.imports
2806
+ async function getUnlistedScriptEntrypoint(config, path10) {
2807
+ const name = getEntrypointName(config.entrypointsDir, path10);
2808
+ const defaultExport = await importEntrypointFile(
2809
+ path10,
2810
+ config
2754
2811
  );
2755
- }
2756
-
2757
- // src/core/vite-plugins/unimport.ts
2758
- var import_path2 = require("path");
2759
- var ENABLED_EXTENSIONS = /* @__PURE__ */ new Set([
2760
- ".js",
2761
- ".jsx",
2762
- ".ts",
2763
- ".tsx",
2764
- ".vue",
2765
- ".svelte"
2766
- ]);
2767
- function unimport(config) {
2768
- const options = getUnimportOptions(config);
2769
- if (options === false)
2770
- return [];
2771
- const unimport2 = (0, import_unimport.createUnimport)(options);
2812
+ if (defaultExport == null) {
2813
+ throw Error(
2814
+ `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
2815
+ );
2816
+ }
2817
+ const { main: _, ...moduleOptions } = defaultExport;
2818
+ const options = moduleOptions;
2772
2819
  return {
2773
- name: "wxt:unimport",
2774
- async config() {
2775
- await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
2776
- },
2777
- async transform(code, id) {
2778
- if (id.includes("node_modules"))
2779
- return;
2780
- if (!ENABLED_EXTENSIONS.has((0, import_path2.extname)(id)))
2781
- return;
2782
- return unimport2.injectImports(code, id);
2783
- }
2820
+ type: "unlisted-script",
2821
+ name,
2822
+ inputPath: path10,
2823
+ outputDir: config.outDir,
2824
+ options
2784
2825
  };
2785
2826
  }
2786
-
2787
- // src/core/vite-plugins/virtualEntrypoint.ts
2788
- var import_fs_extra2 = __toESM(require("fs-extra"), 1);
2789
- var import_path3 = require("path");
2790
- function virtualEntrypoint(type, config) {
2791
- const virtualId = `virtual:wxt-${type}?`;
2792
- const resolvedVirtualId = `\0${virtualId}`;
2793
- return {
2794
- name: `wxt:virtual-entrypoint`,
2795
- resolveId(id) {
2796
- const index = id.indexOf(virtualId);
2797
- if (index === -1)
2798
- return;
2799
- const inputPath = normalizePath(id.substring(index + virtualId.length));
2800
- return resolvedVirtualId + inputPath;
2801
- },
2802
- async load(id) {
2803
- if (!id.startsWith(resolvedVirtualId))
2804
- return;
2805
- const inputPath = id.replace(resolvedVirtualId, "");
2806
- const template = await import_fs_extra2.default.readFile(
2807
- (0, import_path3.resolve)(
2808
- config.root,
2809
- `node_modules/wxt/dist/virtual/${type}-entrypoint.js`
2810
- ),
2811
- "utf-8"
2827
+ async function getBackgroundEntrypoint(config, path10) {
2828
+ const name = "background";
2829
+ let options = {};
2830
+ if (path10 !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
2831
+ const defaultExport = await importEntrypointFile(
2832
+ path10,
2833
+ config
2834
+ );
2835
+ if (defaultExport == null) {
2836
+ throw Error(
2837
+ `${name}: Default export not found, did you forget to call "export default defineBackground(...)"?`
2812
2838
  );
2813
- return template.replace(`virtual:user-${type}`, inputPath);
2814
2839
  }
2815
- };
2816
- }
2817
-
2818
- // src/core/vite-plugins/tsconfigPaths.ts
2819
- function tsconfigPaths(config) {
2840
+ const { main: _, ...moduleOptions } = defaultExport;
2841
+ options = moduleOptions;
2842
+ }
2820
2843
  return {
2821
- name: "wxt:aliases",
2822
- async config() {
2823
- return {
2824
- resolve: {
2825
- alias: config.alias
2826
- }
2827
- };
2844
+ type: "background",
2845
+ name,
2846
+ inputPath: path10,
2847
+ outputDir: config.outDir,
2848
+ options: {
2849
+ ...options,
2850
+ type: resolvePerBrowserOption(options.type, config.browser),
2851
+ persistent: resolvePerBrowserOption(options.persistent, config.browser)
2828
2852
  }
2829
2853
  };
2830
2854
  }
2831
-
2832
- // src/core/vite-plugins/noopBackground.ts
2833
- function noopBackground() {
2834
- const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
2835
- const resolvedVirtualModuleId = "\0" + virtualModuleId;
2855
+ async function getContentScriptEntrypoint(config, path10) {
2856
+ const name = getEntrypointName(config.entrypointsDir, path10);
2857
+ const { main: _, ...options } = await importEntrypointFile(path10, config);
2858
+ if (options == null) {
2859
+ throw Error(
2860
+ `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
2861
+ );
2862
+ }
2836
2863
  return {
2837
- name: "wxt:noop-background",
2838
- resolveId(id) {
2839
- if (id === virtualModuleId)
2840
- return resolvedVirtualModuleId;
2841
- },
2842
- load(id) {
2843
- if (id === resolvedVirtualModuleId) {
2844
- return `import { defineBackground } from 'wxt/client';
2845
- export default defineBackground(() => void 0)`;
2846
- }
2847
- }
2864
+ type: "content-script",
2865
+ name,
2866
+ inputPath: path10,
2867
+ outputDir: (0, import_path2.resolve)(config.outDir, CONTENT_SCRIPT_OUT_DIR),
2868
+ options
2848
2869
  };
2849
2870
  }
2850
- var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
2871
+ var PATH_GLOB_TO_TYPE_MAP = {
2872
+ "sandbox.html": "sandbox",
2873
+ "sandbox/index.html": "sandbox",
2874
+ "*.sandbox.html": "sandbox",
2875
+ "*.sandbox/index.html": "sandbox",
2876
+ "bookmarks.html": "bookmarks",
2877
+ "bookmarks/index.html": "bookmarks",
2878
+ "history.html": "history",
2879
+ "history/index.html": "history",
2880
+ "newtab.html": "newtab",
2881
+ "newtab/index.html": "newtab",
2882
+ "sidepanel.html": "sidepanel",
2883
+ "sidepanel/index.html": "sidepanel",
2884
+ "*.sidepanel.html": "sidepanel",
2885
+ "*.sidepanel/index.html": "sidepanel",
2886
+ "devtools.html": "devtools",
2887
+ "devtools/index.html": "devtools",
2888
+ "background.[jt]s": "background",
2889
+ "background/index.[jt]s": "background",
2890
+ [VIRTUAL_NOOP_BACKGROUND_MODULE_ID]: "background",
2891
+ "content.[jt]s?(x)": "content-script",
2892
+ "content/index.[jt]s?(x)": "content-script",
2893
+ "*.content.[jt]s?(x)": "content-script",
2894
+ "*.content/index.[jt]s?(x)": "content-script",
2895
+ [`content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2896
+ [`*.content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2897
+ [`content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2898
+ [`*.content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2899
+ "popup.html": "popup",
2900
+ "popup/index.html": "popup",
2901
+ "options.html": "options",
2902
+ "options/index.html": "options",
2903
+ "*.html": "unlisted-page",
2904
+ "*/index.html": "unlisted-page",
2905
+ "*.[jt]s": "unlisted-script",
2906
+ "*/index.ts": "unlisted-script",
2907
+ [`*.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
2908
+ [`*/index.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
2909
+ // Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files or tests
2910
+ "*/**": "ignored"
2911
+ };
2912
+ var CONTENT_SCRIPT_OUT_DIR = "content-scripts";
2851
2913
 
2852
- // src/core/vite-plugins/cssEntrypoints.ts
2853
- function cssEntrypoints(entrypoint, config) {
2854
- return {
2855
- name: "wxt:css-entrypoint",
2856
- config() {
2857
- return {
2858
- build: {
2859
- rollupOptions: {
2860
- output: {
2861
- assetFileNames: () => getEntrypointBundlePath(entrypoint, config.outDir, ".css")
2862
- }
2863
- }
2864
- }
2865
- };
2866
- },
2867
- generateBundle(_, bundle) {
2868
- Object.keys(bundle).forEach((file) => {
2869
- if (file.endsWith(".js"))
2870
- delete bundle[file];
2871
- });
2872
- }
2873
- };
2874
- }
2914
+ // src/core/utils/building/generate-wxt-dir.ts
2915
+ var import_unimport = require("unimport");
2916
+ var import_fs_extra4 = __toESM(require("fs-extra"), 1);
2917
+ var import_path3 = require("path");
2875
2918
 
2876
- // src/core/vite-plugins/bundleAnalysis.ts
2877
- var import_rollup_plugin_visualizer = require("rollup-plugin-visualizer");
2878
- var increment = 0;
2879
- function bundleAnalysis() {
2880
- return (0, import_rollup_plugin_visualizer.visualizer)({
2881
- emitFile: true,
2882
- template: "raw-data",
2883
- filename: `stats-${increment++}.json`
2884
- });
2919
+ // src/core/utils/unimport.ts
2920
+ var import_defu = require("defu");
2921
+ function getUnimportOptions(config) {
2922
+ if (config.imports === false)
2923
+ return false;
2924
+ const defaultOptions = {
2925
+ debugLog: config.logger.debug,
2926
+ imports: [
2927
+ { name: "defineConfig", from: "wxt" },
2928
+ { name: "fakeBrowser", from: "wxt/testing" }
2929
+ ],
2930
+ presets: [
2931
+ { package: "wxt/client" },
2932
+ { package: "wxt/browser" },
2933
+ { package: "wxt/sandbox" },
2934
+ { package: "wxt/storage" }
2935
+ ],
2936
+ warn: config.logger.warn,
2937
+ dirs: ["components", "composables", "hooks", "utils"]
2938
+ };
2939
+ return (0, import_defu.defu)(config.imports, defaultOptions);
2885
2940
  }
2886
2941
 
2887
2942
  // src/core/utils/globals.ts
@@ -2929,7 +2984,7 @@ function getGlobals(config) {
2929
2984
  }
2930
2985
  ];
2931
2986
  }
2932
- function getEntrypointGlobals(config, entrypointName) {
2987
+ function getEntrypointGlobals(entrypointName) {
2933
2988
  return [
2934
2989
  {
2935
2990
  name: surroundInUnderscore("ENTRYPOINT"),
@@ -2942,859 +2997,902 @@ function surroundInUnderscore(name) {
2942
2997
  return `__${name}__`;
2943
2998
  }
2944
2999
 
2945
- // src/core/vite-plugins/globals.ts
2946
- function globals(config) {
2947
- return {
2948
- name: "wxt:globals",
2949
- config() {
2950
- const define = {};
2951
- for (const global3 of getGlobals(config)) {
2952
- define[global3.name] = JSON.stringify(global3.value);
2953
- }
2954
- return {
2955
- define
2956
- };
2957
- }
2958
- };
2959
- }
2960
-
2961
- // src/core/vite-plugins/webextensionPolyfillAlias.ts
2962
- var import_node_path4 = __toESM(require("path"), 1);
3000
+ // src/core/utils/building/generate-wxt-dir.ts
3001
+ var import_node_path3 = __toESM(require("path"), 1);
2963
3002
 
2964
- // src/core/vite-plugins/excludeBrowserPolyfill.ts
2965
- function excludeBrowserPolyfill(config) {
2966
- const virtualId = "virtual:wxt-webextension-polyfill-disabled";
2967
- return {
2968
- name: "wxt:exclude-browser-polyfill",
2969
- config() {
2970
- if (config.experimental.includeBrowserPolyfill)
2971
- return;
2972
- return {
2973
- resolve: {
2974
- alias: {
2975
- "webextension-polyfill": virtualId
2976
- }
2977
- }
2978
- };
2979
- },
2980
- load(id) {
2981
- if (id === virtualId) {
2982
- return "export default chrome";
2983
- }
2984
- }
2985
- };
3003
+ // src/core/utils/i18n.ts
3004
+ var predefinedMessages = {
3005
+ "@@extension_id": {
3006
+ message: "<browser.runtime.id>",
3007
+ 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."
3008
+ },
3009
+ "@@ui_locale": {
3010
+ message: "<browser.i18n.getUiLocale()>",
3011
+ description: ""
3012
+ },
3013
+ "@@bidi_dir": {
3014
+ message: "<ltr|rtl>",
3015
+ description: 'The text direction for the current locale, either "ltr" for left-to-right languages such as English or "rtl" for right-to-left languages such as Japanese.'
3016
+ },
3017
+ "@@bidi_reversed_dir": {
3018
+ message: "<rtl|ltr>",
3019
+ description: `If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".`
3020
+ },
3021
+ "@@bidi_start_edge": {
3022
+ message: "<left|right>",
3023
+ description: `If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".`
3024
+ },
3025
+ "@@bidi_end_edge": {
3026
+ message: "<right|left>",
3027
+ description: `If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".`
3028
+ }
3029
+ };
3030
+ function parseI18nMessages(messagesJson) {
3031
+ return Object.entries({
3032
+ ...predefinedMessages,
3033
+ ...messagesJson
3034
+ }).map(([name, details]) => ({
3035
+ name,
3036
+ ...details
3037
+ }));
2986
3038
  }
2987
3039
 
2988
- // src/core/utils/fs.ts
2989
- var import_fs_extra3 = __toESM(require("fs-extra"), 1);
2990
- var import_fast_glob = __toESM(require("fast-glob"), 1);
2991
- var import_node_path5 = __toESM(require("path"), 1);
2992
- async function writeFileIfDifferent(file, newContents) {
2993
- const existingContents = await import_fs_extra3.default.readFile(file, "utf-8").catch(() => void 0);
2994
- if (existingContents !== newContents) {
2995
- await import_fs_extra3.default.writeFile(file, newContents);
3040
+ // src/core/utils/building/generate-wxt-dir.ts
3041
+ async function generateTypesDir(entrypoints, config) {
3042
+ await import_fs_extra4.default.ensureDir(config.typesDir);
3043
+ const references = [];
3044
+ const imports = getUnimportOptions(config);
3045
+ if (imports !== false) {
3046
+ references.push(await writeImportsDeclarationFile(config, imports));
2996
3047
  }
3048
+ references.push(await writePathsDeclarationFile(entrypoints, config));
3049
+ references.push(await writeI18nDeclarationFile(config));
3050
+ references.push(await writeGlobalsDeclarationFile(config));
3051
+ const mainReference = await writeMainDeclarationFile(references, config);
3052
+ await writeTsConfigFile(mainReference, config);
2997
3053
  }
2998
- async function getPublicFiles(config) {
2999
- if (!await import_fs_extra3.default.exists(config.publicDir))
3000
- return [];
3001
- const files = await (0, import_fast_glob.default)("**/*", { cwd: config.publicDir });
3002
- return files.map(unnormalizePath);
3054
+ async function writeImportsDeclarationFile(config, unimportOptions) {
3055
+ const filePath = (0, import_path3.resolve)(config.typesDir, "imports.d.ts");
3056
+ const unimport2 = (0, import_unimport.createUnimport)(unimportOptions);
3057
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
3058
+ await writeFileIfDifferent(
3059
+ filePath,
3060
+ ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
3061
+ "\n"
3062
+ ) + "\n"
3063
+ );
3064
+ return filePath;
3003
3065
  }
3004
- async function removeEmptyDirs(dir) {
3005
- const files = await import_fs_extra3.default.readdir(dir);
3006
- for (const file of files) {
3007
- const filePath = import_node_path5.default.join(dir, file);
3008
- const stats = await import_fs_extra3.default.stat(filePath);
3009
- if (stats.isDirectory()) {
3010
- await removeEmptyDirs(filePath);
3011
- }
3012
- }
3013
- try {
3014
- await import_fs_extra3.default.rmdir(dir);
3015
- } catch {
3066
+ async function writePathsDeclarationFile(entrypoints, config) {
3067
+ const filePath = (0, import_path3.resolve)(config.typesDir, "paths.d.ts");
3068
+ const unions = entrypoints.map(
3069
+ (entry) => getEntrypointBundlePath(
3070
+ entry,
3071
+ config.outDir,
3072
+ entry.inputPath.endsWith(".html") ? ".html" : ".js"
3073
+ )
3074
+ ).concat(await getPublicFiles(config)).map(normalizePath).map((path10) => ` | "/${path10}"`).sort().join("\n");
3075
+ const template = `// Generated by wxt
3076
+ import "wxt/browser";
3077
+
3078
+ declare module "wxt/browser" {
3079
+ export type PublicPath =
3080
+ {{ union }}
3081
+ export interface WxtRuntime extends Runtime.Static {
3082
+ getURL(path: PublicPath): string;
3016
3083
  }
3017
3084
  }
3085
+ `;
3086
+ await writeFileIfDifferent(
3087
+ filePath,
3088
+ template.replace("{{ union }}", unions || " | never")
3089
+ );
3090
+ return filePath;
3091
+ }
3092
+ async function writeI18nDeclarationFile(config) {
3093
+ const filePath = (0, import_path3.resolve)(config.typesDir, "i18n.d.ts");
3094
+ const defaultLocale = config.manifest.default_locale;
3095
+ const template = `// Generated by wxt
3096
+ import "wxt/browser";
3018
3097
 
3019
- // src/core/utils/building/build-entrypoints.ts
3020
- var import_fs_extra4 = __toESM(require("fs-extra"), 1);
3021
- var import_path4 = require("path");
3022
- var import_picocolors = __toESM(require("picocolors"), 1);
3023
- async function buildEntrypoints(groups, config, spinner) {
3024
- const steps = [];
3025
- for (let i = 0; i < groups.length; i++) {
3026
- const group = groups[i];
3027
- spinner.text = import_picocolors.default.dim(`[${i + 1}/${groups.length}]`) + ` ${[group].flat().map((e) => e.name).join(import_picocolors.default.dim(", "))}`;
3028
- const step = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config);
3029
- steps.push(step);
3098
+ declare module "wxt/browser" {
3099
+ /**
3100
+ * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
3101
+ */
3102
+ interface GetMessageOptions {
3103
+ /**
3104
+ * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
3105
+ */
3106
+ escapeLt?: boolean
3030
3107
  }
3031
- const publicAssets = await copyPublicDirectory(config);
3032
- await removeEmptyDirs(config.outDir);
3033
- return { publicAssets, steps };
3034
- }
3035
- async function buildSingleEntrypoint(entrypoint, config) {
3036
- const isVirtual = [
3037
- "background",
3038
- "content-script",
3039
- "unlisted-script"
3040
- ].includes(entrypoint.type);
3041
- const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
3042
- const plugins = [];
3043
- if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
3044
- plugins.push(cssEntrypoints(entrypoint, config));
3108
+
3109
+ export interface WxtI18n extends I18n.Static {
3110
+ {{ overrides }}
3045
3111
  }
3046
- const libMode = {
3047
- mode: config.mode,
3048
- plugins,
3049
- build: {
3050
- lib: {
3051
- entry,
3052
- formats: ["iife"],
3053
- name: "_",
3054
- fileName: entrypoint.name
3055
- },
3056
- rollupOptions: {
3057
- output: {
3058
- // There's only a single output for this build, so we use the desired bundle path for the
3059
- // entry output (like "content-scripts/overlay.js")
3060
- entryFileNames: getEntrypointBundlePath(
3061
- entrypoint,
3062
- config.outDir,
3063
- ".js"
3064
- ),
3065
- // Output content script CSS to `content-scripts/`, but all other scripts are written to
3066
- // `assets/`.
3067
- assetFileNames: ({ name }) => {
3068
- if (entrypoint.type === "content-script" && name?.endsWith("css")) {
3069
- return `content-scripts/${entrypoint.name}.[ext]`;
3070
- } else {
3071
- return `assets/${entrypoint.name}.[ext]`;
3072
- }
3073
- }
3074
- }
3075
- }
3076
- },
3077
- define: {
3078
- // See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
3079
- "process.env.NODE_ENV": JSON.stringify(config.mode)
3080
- }
3081
- };
3082
- for (const global3 of getEntrypointGlobals(config, entrypoint.name)) {
3083
- libMode.define[global3.name] = JSON.stringify(global3.value);
3112
+ }
3113
+ `;
3114
+ let messages;
3115
+ if (defaultLocale) {
3116
+ const defaultLocalePath = import_node_path3.default.resolve(
3117
+ config.publicDir,
3118
+ "_locales",
3119
+ defaultLocale,
3120
+ "messages.json"
3121
+ );
3122
+ const content = JSON.parse(await import_fs_extra4.default.readFile(defaultLocalePath, "utf-8"));
3123
+ messages = parseI18nMessages(content);
3124
+ } else {
3125
+ messages = parseI18nMessages({});
3084
3126
  }
3085
- const entryConfig = vite.mergeConfig(
3086
- libMode,
3087
- await config.vite(config.env)
3127
+ const overrides = messages.map((message) => {
3128
+ return ` /**
3129
+ * ${message.description ?? "No message description."}
3130
+ *
3131
+ * "${message.message}"
3132
+ */
3133
+ getMessage(
3134
+ messageName: "${message.name}",
3135
+ substitutions?: string | string[],
3136
+ options?: GetMessageOptions,
3137
+ ): string;`;
3138
+ });
3139
+ await writeFileIfDifferent(
3140
+ filePath,
3141
+ template.replace("{{ overrides }}", overrides.join("\n"))
3088
3142
  );
3089
- const result = await vite.build(entryConfig);
3090
- return {
3091
- entrypoints: entrypoint,
3092
- chunks: getBuildOutputChunks(result)
3093
- };
3143
+ return filePath;
3094
3144
  }
3095
- async function buildMultipleEntrypoints(entrypoints, config) {
3096
- const multiPage = {
3097
- mode: config.mode,
3098
- plugins: [multipageMove(entrypoints, config)],
3099
- build: {
3100
- rollupOptions: {
3101
- input: entrypoints.reduce((input, entry) => {
3102
- input[entry.name] = entry.inputPath;
3103
- return input;
3104
- }, {}),
3105
- output: {
3106
- // Include a hash to prevent conflicts
3107
- chunkFileNames: "chunks/[name]-[hash].js",
3108
- // Include a hash to prevent conflicts
3109
- entryFileNames: "chunks/[name]-[hash].js",
3110
- // We can't control the "name", so we need a hash to prevent conflicts
3111
- assetFileNames: "assets/[name]-[hash].[ext]"
3112
- }
3113
- }
3114
- },
3115
- define: {}
3116
- };
3117
- for (const global3 of getEntrypointGlobals(config, "html")) {
3118
- multiPage.define[global3.name] = JSON.stringify(global3.value);
3119
- }
3120
- const entryConfig = vite.mergeConfig(
3121
- multiPage,
3122
- await config.vite(config.env)
3145
+ async function writeGlobalsDeclarationFile(config) {
3146
+ const filePath = (0, import_path3.resolve)(config.typesDir, "globals.d.ts");
3147
+ const globals2 = [...getGlobals(config), ...getEntrypointGlobals("")];
3148
+ await writeFileIfDifferent(
3149
+ filePath,
3150
+ [
3151
+ "// Generated by wxt",
3152
+ "export {}",
3153
+ "declare global {",
3154
+ ...globals2.map((global3) => ` const ${global3.name}: ${global3.type};`),
3155
+ "}"
3156
+ ].join("\n") + "\n"
3123
3157
  );
3124
- const result = await vite.build(entryConfig);
3125
- return {
3126
- entrypoints,
3127
- chunks: getBuildOutputChunks(result)
3128
- };
3158
+ return filePath;
3129
3159
  }
3130
- function getBuildOutputChunks(result) {
3131
- if ("on" in result)
3132
- throw Error("wxt does not support vite watch mode.");
3133
- if (Array.isArray(result))
3134
- return result.flatMap(({ output }) => output);
3135
- return result.output;
3160
+ async function writeMainDeclarationFile(references, config) {
3161
+ const dir = config.wxtDir;
3162
+ const filePath = (0, import_path3.resolve)(dir, "wxt.d.ts");
3163
+ await writeFileIfDifferent(
3164
+ filePath,
3165
+ [
3166
+ "// Generated by wxt",
3167
+ `/// <reference types="vite/client" />`,
3168
+ ...references.map(
3169
+ (ref) => `/// <reference types="./${normalizePath((0, import_path3.relative)(dir, ref))}" />`
3170
+ )
3171
+ ].join("\n") + "\n"
3172
+ );
3173
+ return filePath;
3136
3174
  }
3137
- async function copyPublicDirectory(config) {
3138
- const files = await getPublicFiles(config);
3139
- if (files.length === 0)
3140
- return [];
3141
- const publicAssets = [];
3142
- for (const file of files) {
3143
- const srcPath = (0, import_path4.resolve)(config.publicDir, file);
3144
- const outPath = (0, import_path4.resolve)(config.outDir, file);
3145
- await import_fs_extra4.default.ensureDir((0, import_path4.dirname)(outPath));
3146
- await import_fs_extra4.default.copyFile(srcPath, outPath);
3147
- publicAssets.push({
3148
- type: "asset",
3149
- fileName: file,
3150
- name: file,
3151
- needsCodeReference: false,
3152
- source: await import_fs_extra4.default.readFile(srcPath)
3153
- });
3154
- }
3155
- return publicAssets;
3175
+ async function writeTsConfigFile(mainReference, config) {
3176
+ const dir = config.wxtDir;
3177
+ const getTsconfigPath = (path10) => normalizePath((0, import_path3.relative)(dir, path10));
3178
+ const paths = Object.entries(config.alias).flatMap(([alias, absolutePath]) => {
3179
+ const aliasPath = getTsconfigPath(absolutePath);
3180
+ return [
3181
+ ` "${alias}": ["${aliasPath}"]`,
3182
+ ` "${alias}/*": ["${aliasPath}/*"]`
3183
+ ];
3184
+ }).join(",\n");
3185
+ await writeFileIfDifferent(
3186
+ (0, import_path3.resolve)(dir, "tsconfig.json"),
3187
+ `{
3188
+ "compilerOptions": {
3189
+ "target": "ESNext",
3190
+ "module": "ESNext",
3191
+ "moduleResolution": "Bundler",
3192
+ "noEmit": true,
3193
+ "esModuleInterop": true,
3194
+ "forceConsistentCasingInFileNames": true,
3195
+ "resolveJsonModule": true,
3196
+ "strict": true,
3197
+ "skipLibCheck": true,
3198
+ "paths": {
3199
+ ${paths}
3200
+ }
3201
+ },
3202
+ "include": [
3203
+ "${getTsconfigPath(config.root)}/**/*",
3204
+ "./${getTsconfigPath(mainReference)}"
3205
+ ],
3206
+ "exclude": ["${getTsconfigPath(config.outBaseDir)}"]
3207
+ }`
3208
+ );
3156
3209
  }
3157
3210
 
3158
- // src/core/utils/arrays.ts
3159
- function every(array, predicate) {
3160
- for (let i = 0; i < array.length; i++)
3161
- if (!predicate(array[i], i))
3162
- return false;
3163
- return true;
3164
- }
3211
+ // src/core/utils/building/get-internal-config.ts
3212
+ var import_c12 = require("c12");
3213
+ var import_node_path7 = __toESM(require("path"), 1);
3165
3214
 
3166
- // src/core/utils/building/detect-dev-changes.ts
3167
- function detectDevChanges(changedFiles, currentOutput) {
3168
- if (currentOutput == null)
3169
- return { type: "no-change" };
3170
- const changedSteps = new Set(
3171
- changedFiles.flatMap(
3172
- (changedFile) => findEffectedSteps(changedFile, currentOutput)
3173
- )
3174
- );
3175
- if (changedSteps.size === 0)
3176
- return { type: "no-change" };
3177
- const unchangedOutput = {
3178
- manifest: currentOutput.manifest,
3179
- steps: [],
3180
- publicAssets: []
3181
- };
3182
- const changedOutput = {
3183
- manifest: currentOutput.manifest,
3184
- steps: [],
3185
- publicAssets: []
3186
- };
3187
- for (const step of currentOutput.steps) {
3188
- if (changedSteps.has(step)) {
3189
- changedOutput.steps.push(step);
3190
- } else {
3191
- unchangedOutput.steps.push(step);
3192
- }
3193
- }
3194
- for (const asset of currentOutput.publicAssets) {
3195
- if (changedSteps.has(asset)) {
3196
- changedOutput.publicAssets.push(asset);
3197
- } else {
3198
- unchangedOutput.publicAssets.push(asset);
3199
- }
3200
- }
3201
- const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, ([_, file]) => file.endsWith(".html"));
3202
- if (isOnlyHtmlChanges) {
3203
- return {
3204
- type: "html-reload",
3205
- cachedOutput: unchangedOutput,
3206
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
3207
- };
3208
- }
3209
- const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
3210
- changedOutput.steps.flatMap((step) => step.entrypoints),
3211
- (entry) => entry.type === "content-script"
3212
- );
3213
- if (isOnlyContentScripts) {
3214
- return {
3215
- type: "content-script-reload",
3216
- cachedOutput: unchangedOutput,
3217
- changedSteps: changedOutput.steps,
3218
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
3219
- };
3220
- }
3221
- return {
3222
- type: "extension-reload",
3223
- cachedOutput: unchangedOutput,
3224
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
3225
- };
3226
- }
3227
- function findEffectedSteps(changedFile, currentOutput) {
3228
- const changes = [];
3229
- const changedPath = normalizePath(changedFile[1]);
3230
- const isChunkEffected = (chunk) => (
3231
- // If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
3232
- // fileName is normalized, relative bundle path
3233
- chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
3234
- // moduleIds are absolute, normalized paths
3235
- chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
3236
- );
3237
- for (const step of currentOutput.steps) {
3238
- const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
3239
- if (effectedChunk)
3240
- changes.push(step);
3241
- }
3242
- const effectedAsset = currentOutput.publicAssets.find(
3243
- (chunk) => isChunkEffected(chunk)
3244
- );
3245
- if (effectedAsset)
3246
- changes.push(effectedAsset);
3247
- return changes;
3248
- }
3249
-
3250
- // src/core/utils/building/find-entrypoints.ts
3251
- var import_path5 = require("path");
3215
+ // src/core/utils/cache.ts
3252
3216
  var import_fs_extra5 = __toESM(require("fs-extra"), 1);
3253
- var import_minimatch = require("minimatch");
3254
- var import_linkedom2 = require("linkedom");
3255
- var import_json5 = __toESM(require("json5"), 1);
3256
- var import_fast_glob2 = __toESM(require("fast-glob"), 1);
3257
- async function findEntrypoints(config) {
3258
- const relativePaths = await (0, import_fast_glob2.default)("**/*", {
3259
- cwd: config.entrypointsDir
3260
- });
3261
- relativePaths.sort();
3262
- const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
3263
- let hasBackground = false;
3264
- const possibleEntrypoints = await Promise.all(
3265
- relativePaths.map(async (relativePath) => {
3266
- const path11 = (0, import_path5.resolve)(config.entrypointsDir, relativePath);
3267
- const matchingGlob = pathGlobs.find(
3268
- (glob5) => (0, import_minimatch.minimatch)(relativePath, glob5)
3269
- );
3270
- if (matchingGlob == null) {
3271
- config.logger.warn(
3272
- `${relativePath} does not match any known entrypoint. Known entrypoints:
3273
- ${JSON.stringify(
3274
- PATH_GLOB_TO_TYPE_MAP,
3275
- null,
3276
- 2
3277
- )}`
3278
- );
3279
- return;
3280
- }
3281
- const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
3282
- if (type === "ignored")
3283
- return;
3284
- switch (type) {
3285
- case "popup":
3286
- return await getPopupEntrypoint(config, path11);
3287
- case "options":
3288
- return await getOptionsEntrypoint(config, path11);
3289
- case "background":
3290
- hasBackground = true;
3291
- return await getBackgroundEntrypoint(config, path11);
3292
- case "content-script":
3293
- return await getContentScriptEntrypoint(config, path11);
3294
- case "unlisted-page":
3295
- return await getUnlistedPageEntrypoint(config, path11);
3296
- case "unlisted-script":
3297
- return await getUnlistedScriptEntrypoint(config, path11);
3298
- case "content-script-style":
3299
- return {
3300
- type,
3301
- name: getEntrypointName(config.entrypointsDir, path11),
3302
- inputPath: path11,
3303
- outputDir: (0, import_path5.resolve)(config.outDir, CONTENT_SCRIPT_OUT_DIR),
3304
- options: {
3305
- include: void 0,
3306
- exclude: void 0
3307
- }
3308
- };
3309
- default:
3310
- return {
3311
- type,
3312
- name: getEntrypointName(config.entrypointsDir, path11),
3313
- inputPath: path11,
3314
- outputDir: config.outDir,
3315
- options: {
3316
- include: void 0,
3317
- exclude: void 0
3318
- }
3319
- };
3320
- }
3321
- })
3322
- );
3323
- const entrypoints = possibleEntrypoints.filter(
3324
- (entry) => !!entry
3325
- );
3326
- const existingNames = {};
3327
- entrypoints.forEach((entrypoint) => {
3328
- const withSameName = existingNames[entrypoint.name];
3329
- if (withSameName) {
3330
- throw Error(
3331
- `Multiple entrypoints with the name "${entrypoint.name}" detected, but only one is allowed: ${[
3332
- (0, import_path5.relative)(config.root, withSameName.inputPath),
3333
- (0, import_path5.relative)(config.root, entrypoint.inputPath)
3334
- ].join(", ")}`
3335
- );
3336
- }
3337
- existingNames[entrypoint.name] = entrypoint;
3338
- });
3339
- if (config.command === "serve" && !hasBackground) {
3340
- entrypoints.push(
3341
- await getBackgroundEntrypoint(config, VIRTUAL_NOOP_BACKGROUND_MODULE_ID)
3342
- );
3343
- }
3344
- config.logger.debug("All entrypoints:", entrypoints);
3345
- const targetEntrypoints = entrypoints.filter((entry) => {
3346
- const { include, exclude } = entry.options;
3347
- if (include?.length && exclude?.length) {
3348
- config.logger.warn(
3349
- `The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`
3350
- );
3351
- return false;
3352
- }
3353
- if (exclude?.length && !include?.length) {
3354
- return !exclude.includes(config.browser);
3355
- }
3356
- if (include?.length && !exclude?.length) {
3357
- return include.includes(config.browser);
3358
- }
3359
- return true;
3360
- });
3361
- config.logger.debug(`${config.browser} entrypoints:`, targetEntrypoints);
3362
- return targetEntrypoints;
3363
- }
3364
- function getHtmlBaseOptions(document) {
3365
- const options = {};
3366
- const includeContent = document.querySelector("meta[name='manifest.include']")?.getAttribute("content");
3367
- if (includeContent) {
3368
- options.include = import_json5.default.parse(includeContent);
3369
- }
3370
- const excludeContent = document.querySelector("meta[name='manifest.exclude']")?.getAttribute("content");
3371
- if (excludeContent) {
3372
- options.exclude = import_json5.default.parse(excludeContent);
3373
- }
3374
- return options;
3375
- }
3376
- async function getPopupEntrypoint(config, path11) {
3377
- const content = await import_fs_extra5.default.readFile(path11, "utf-8");
3378
- const { document } = (0, import_linkedom2.parseHTML)(content);
3379
- const options = getHtmlBaseOptions(document);
3380
- const title = document.querySelector("title");
3381
- if (title != null)
3382
- options.defaultTitle = title.textContent ?? void 0;
3383
- const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
3384
- if (defaultIconContent) {
3385
- try {
3386
- options.defaultIcon = import_json5.default.parse(defaultIconContent);
3387
- } catch (err) {
3388
- config.logger.fatal(
3389
- `Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
3390
- err
3391
- );
3392
- }
3393
- }
3394
- const mv2TypeContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
3395
- if (mv2TypeContent) {
3396
- options.mv2Key = mv2TypeContent === "page_action" ? "page_action" : "browser_action";
3397
- }
3398
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
3399
- if (browserStyleContent) {
3400
- options.browserStyle = browserStyleContent === "true";
3401
- }
3402
- return {
3403
- type: "popup",
3404
- name: "popup",
3405
- options,
3406
- inputPath: path11,
3407
- outputDir: config.outDir
3408
- };
3409
- }
3410
- async function getOptionsEntrypoint(config, path11) {
3411
- const content = await import_fs_extra5.default.readFile(path11, "utf-8");
3412
- const { document } = (0, import_linkedom2.parseHTML)(content);
3413
- const options = getHtmlBaseOptions(document);
3414
- const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
3415
- if (openInTabContent) {
3416
- options.openInTab = openInTabContent === "true";
3417
- }
3418
- const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
3419
- if (chromeStyleContent) {
3420
- options.chromeStyle = chromeStyleContent === "true";
3421
- }
3422
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
3423
- if (browserStyleContent) {
3424
- options.browserStyle = browserStyleContent === "true";
3425
- }
3426
- return {
3427
- type: "options",
3428
- name: "options",
3429
- options,
3430
- inputPath: path11,
3431
- outputDir: config.outDir
3432
- };
3433
- }
3434
- async function getUnlistedPageEntrypoint(config, path11) {
3435
- const content = await import_fs_extra5.default.readFile(path11, "utf-8");
3436
- const { document } = (0, import_linkedom2.parseHTML)(content);
3437
- return {
3438
- type: "unlisted-page",
3439
- name: getEntrypointName(config.entrypointsDir, path11),
3440
- inputPath: path11,
3441
- outputDir: config.outDir,
3442
- options: getHtmlBaseOptions(document)
3443
- };
3444
- }
3445
- async function getUnlistedScriptEntrypoint(config, path11) {
3446
- const name = getEntrypointName(config.entrypointsDir, path11);
3447
- const defaultExport = await importEntrypointFile(
3448
- path11,
3449
- config
3450
- );
3451
- if (defaultExport == null) {
3452
- throw Error(
3453
- `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
3454
- );
3455
- }
3456
- const { main: _, ...moduleOptions } = defaultExport;
3457
- const options = moduleOptions;
3458
- return {
3459
- type: "unlisted-script",
3460
- name,
3461
- inputPath: path11,
3462
- outputDir: config.outDir,
3463
- options
3464
- };
3465
- }
3466
- async function getBackgroundEntrypoint(config, path11) {
3467
- const name = "background";
3468
- let options = {};
3469
- if (path11 !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
3470
- const defaultExport = await importEntrypointFile(
3471
- path11,
3472
- config
3473
- );
3474
- if (defaultExport == null) {
3475
- throw Error(
3476
- `${name}: Default export not found, did you forget to call "export default defineBackground(...)"?`
3477
- );
3478
- }
3479
- const { main: _, ...moduleOptions } = defaultExport;
3480
- options = moduleOptions;
3481
- }
3217
+ var import_path4 = require("path");
3218
+ function createFsCache(wxtDir) {
3219
+ const getPath = (key) => (0, import_path4.resolve)(wxtDir, "cache", encodeURIComponent(key));
3482
3220
  return {
3483
- type: "background",
3484
- name,
3485
- inputPath: path11,
3486
- outputDir: config.outDir,
3487
- options: {
3488
- ...options,
3489
- type: resolvePerBrowserOption(options.type, config.browser),
3490
- persistent: resolvePerBrowserOption(options.persistent, config.browser)
3221
+ async set(key, value) {
3222
+ const path10 = getPath(key);
3223
+ await (0, import_fs_extra5.ensureDir)((0, import_path4.dirname)(path10));
3224
+ await writeFileIfDifferent(path10, value);
3225
+ },
3226
+ async get(key) {
3227
+ const path10 = getPath(key);
3228
+ try {
3229
+ return await import_fs_extra5.default.readFile(path10, "utf-8");
3230
+ } catch {
3231
+ return void 0;
3232
+ }
3491
3233
  }
3492
3234
  };
3493
3235
  }
3494
- async function getContentScriptEntrypoint(config, path11) {
3495
- const name = getEntrypointName(config.entrypointsDir, path11);
3496
- const { main: _, ...options } = await importEntrypointFile(path11, config);
3497
- if (options == null) {
3498
- throw Error(
3499
- `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
3500
- );
3501
- }
3236
+
3237
+ // src/core/utils/building/get-internal-config.ts
3238
+ var import_consola = __toESM(require("consola"), 1);
3239
+
3240
+ // src/core/builders/vite/plugins/devHtmlPrerender.ts
3241
+ var import_linkedom2 = require("linkedom");
3242
+ var import_node_path4 = require("path");
3243
+ var reactRefreshPreamble = "";
3244
+ function devHtmlPrerender(config) {
3245
+ const htmlReloadId = "@wxt/reload-html";
3246
+ const resolvedHtmlReloadId = (0, import_node_path4.resolve)(
3247
+ config.root,
3248
+ "node_modules/wxt/dist/virtual/reload-html.js"
3249
+ );
3250
+ const virtualReactRefreshId = "@wxt/virtual-react-refresh";
3251
+ const resolvedVirtualReactRefreshId = "\0" + virtualReactRefreshId;
3252
+ return [
3253
+ {
3254
+ apply: "build",
3255
+ name: "wxt:dev-html-prerender",
3256
+ config() {
3257
+ return {
3258
+ resolve: {
3259
+ alias: {
3260
+ [htmlReloadId]: resolvedHtmlReloadId
3261
+ }
3262
+ }
3263
+ };
3264
+ },
3265
+ // Convert scripts like src="./main.tsx" -> src="http://localhost:3000/entrypoints/popup/main.tsx"
3266
+ // before the paths are replaced with their bundled path
3267
+ transform(code, id) {
3268
+ const server = config.server;
3269
+ if (config.command !== "serve" || server == null || !id.endsWith(".html"))
3270
+ return;
3271
+ const { document } = (0, import_linkedom2.parseHTML)(code);
3272
+ const pointToDevServer = (querySelector, attr) => {
3273
+ document.querySelectorAll(querySelector).forEach((element) => {
3274
+ const src = element.getAttribute(attr);
3275
+ if (!src)
3276
+ return;
3277
+ if ((0, import_node_path4.isAbsolute)(src)) {
3278
+ element.setAttribute(attr, server.origin + src);
3279
+ } else if (src.startsWith(".")) {
3280
+ const abs = (0, import_node_path4.resolve)((0, import_node_path4.dirname)(id), src);
3281
+ const pathname = (0, import_node_path4.relative)(config.root, abs);
3282
+ element.setAttribute(attr, `${server.origin}/${pathname}`);
3283
+ }
3284
+ });
3285
+ };
3286
+ pointToDevServer("script[type=module]", "src");
3287
+ pointToDevServer("link[rel=stylesheet]", "href");
3288
+ const reloader = document.createElement("script");
3289
+ reloader.src = htmlReloadId;
3290
+ reloader.type = "module";
3291
+ document.head.appendChild(reloader);
3292
+ const newHtml = document.toString();
3293
+ config.logger.debug("transform " + id);
3294
+ config.logger.debug("Old HTML:\n" + code);
3295
+ config.logger.debug("New HTML:\n" + newHtml);
3296
+ return newHtml;
3297
+ },
3298
+ // Pass the HTML through the dev server to add dev-mode specific code
3299
+ async transformIndexHtml(html, ctx) {
3300
+ const server = config.server;
3301
+ if (config.command !== "serve" || server == null)
3302
+ return;
3303
+ const originalUrl = `${server.origin}${ctx.path}`;
3304
+ const name = getEntrypointName(config.entrypointsDir, ctx.filename);
3305
+ const url2 = `${server.origin}/${name}.html`;
3306
+ const serverHtml = await server.transformHtml(url2, html, originalUrl);
3307
+ const { document } = (0, import_linkedom2.parseHTML)(serverHtml);
3308
+ const reactRefreshScript = Array.from(
3309
+ document.querySelectorAll("script[type=module]")
3310
+ ).find((script) => script.innerHTML.includes("@react-refresh"));
3311
+ if (reactRefreshScript) {
3312
+ reactRefreshPreamble = reactRefreshScript.innerHTML;
3313
+ const virtualScript = document.createElement("script");
3314
+ virtualScript.type = "module";
3315
+ virtualScript.src = `${server.origin}/${virtualReactRefreshId}`;
3316
+ reactRefreshScript.replaceWith(virtualScript);
3317
+ }
3318
+ const viteClientScript = document.querySelector(
3319
+ "script[src='/@vite/client']"
3320
+ );
3321
+ if (viteClientScript) {
3322
+ viteClientScript.src = `${server.origin}${viteClientScript.src}`;
3323
+ }
3324
+ const newHtml = document.toString();
3325
+ config.logger.debug("transformIndexHtml " + ctx.filename);
3326
+ config.logger.debug("Old HTML:\n" + html);
3327
+ config.logger.debug("New HTML:\n" + newHtml);
3328
+ return newHtml;
3329
+ }
3330
+ },
3331
+ {
3332
+ name: "wxt:virtualize-react-refresh",
3333
+ apply: "serve",
3334
+ resolveId(id) {
3335
+ if (id === `/${virtualReactRefreshId}`) {
3336
+ return resolvedVirtualReactRefreshId;
3337
+ }
3338
+ if (id.startsWith("/chunks/")) {
3339
+ return "\0noop";
3340
+ }
3341
+ },
3342
+ load(id) {
3343
+ if (id === resolvedVirtualReactRefreshId) {
3344
+ return reactRefreshPreamble;
3345
+ }
3346
+ if (id === "\0noop") {
3347
+ return "";
3348
+ }
3349
+ }
3350
+ }
3351
+ ];
3352
+ }
3353
+
3354
+ // src/core/builders/vite/plugins/devServerGlobals.ts
3355
+ function devServerGlobals(config) {
3502
3356
  return {
3503
- type: "content-script",
3504
- name,
3505
- inputPath: path11,
3506
- outputDir: (0, import_path5.resolve)(config.outDir, CONTENT_SCRIPT_OUT_DIR),
3507
- options
3357
+ name: "wxt:dev-server-globals",
3358
+ config() {
3359
+ if (config.server == null || config.command == "build")
3360
+ return;
3361
+ return {
3362
+ define: {
3363
+ __DEV_SERVER_PROTOCOL__: JSON.stringify("ws:"),
3364
+ __DEV_SERVER_HOSTNAME__: JSON.stringify(config.server.hostname),
3365
+ __DEV_SERVER_PORT__: JSON.stringify(config.server.port)
3366
+ }
3367
+ };
3368
+ }
3508
3369
  };
3509
3370
  }
3510
- var PATH_GLOB_TO_TYPE_MAP = {
3511
- "sandbox.html": "sandbox",
3512
- "sandbox/index.html": "sandbox",
3513
- "*.sandbox.html": "sandbox",
3514
- "*.sandbox/index.html": "sandbox",
3515
- "bookmarks.html": "bookmarks",
3516
- "bookmarks/index.html": "bookmarks",
3517
- "history.html": "history",
3518
- "history/index.html": "history",
3519
- "newtab.html": "newtab",
3520
- "newtab/index.html": "newtab",
3521
- "sidepanel.html": "sidepanel",
3522
- "sidepanel/index.html": "sidepanel",
3523
- "*.sidepanel.html": "sidepanel",
3524
- "*.sidepanel/index.html": "sidepanel",
3525
- "devtools.html": "devtools",
3526
- "devtools/index.html": "devtools",
3527
- "background.[jt]s": "background",
3528
- "background/index.[jt]s": "background",
3529
- [VIRTUAL_NOOP_BACKGROUND_MODULE_ID]: "background",
3530
- "content.[jt]s?(x)": "content-script",
3531
- "content/index.[jt]s?(x)": "content-script",
3532
- "*.content.[jt]s?(x)": "content-script",
3533
- "*.content/index.[jt]s?(x)": "content-script",
3534
- [`content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
3535
- [`*.content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
3536
- [`content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
3537
- [`*.content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
3538
- "popup.html": "popup",
3539
- "popup/index.html": "popup",
3540
- "options.html": "options",
3541
- "options/index.html": "options",
3542
- "*.html": "unlisted-page",
3543
- "*/index.html": "unlisted-page",
3544
- "*.[jt]s": "unlisted-script",
3545
- "*/index.ts": "unlisted-script",
3546
- [`*.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
3547
- [`*/index.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
3548
- // Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files or tests
3549
- "*/**": "ignored"
3550
- };
3551
- var CONTENT_SCRIPT_OUT_DIR = "content-scripts";
3552
3371
 
3553
- // src/core/utils/building/generate-wxt-dir.ts
3554
- var import_unimport3 = require("unimport");
3555
- var import_fs_extra6 = __toESM(require("fs-extra"), 1);
3556
- var import_path6 = require("path");
3557
- var import_node_path6 = __toESM(require("path"), 1);
3372
+ // src/core/utils/network.ts
3373
+ var import_node_dns = __toESM(require("dns"), 1);
3558
3374
 
3559
- // src/core/utils/i18n.ts
3560
- var predefinedMessages = {
3561
- "@@extension_id": {
3562
- message: "<browser.runtime.id>",
3563
- 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."
3564
- },
3565
- "@@ui_locale": {
3566
- message: "<browser.i18n.getUiLocale()>",
3567
- description: ""
3568
- },
3569
- "@@bidi_dir": {
3570
- message: "<ltr|rtl>",
3571
- description: 'The text direction for the current locale, either "ltr" for left-to-right languages such as English or "rtl" for right-to-left languages such as Japanese.'
3572
- },
3573
- "@@bidi_reversed_dir": {
3574
- message: "<rtl|ltr>",
3575
- description: `If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".`
3576
- },
3577
- "@@bidi_start_edge": {
3578
- message: "<left|right>",
3579
- description: `If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".`
3580
- },
3581
- "@@bidi_end_edge": {
3582
- message: "<right|left>",
3583
- description: `If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".`
3584
- }
3585
- };
3586
- function parseI18nMessages(messagesJson) {
3587
- return Object.entries({
3588
- ...predefinedMessages,
3589
- ...messagesJson
3590
- }).map(([name, details]) => ({
3591
- name,
3592
- ...details
3593
- }));
3375
+ // src/core/utils/time.ts
3376
+ function formatDuration(duration) {
3377
+ if (duration < 1e3)
3378
+ return `${duration} ms`;
3379
+ if (duration < 1e4)
3380
+ return `${(duration / 1e3).toFixed(3)} s`;
3381
+ if (duration < 6e4)
3382
+ return `${(duration / 1e3).toFixed(1)} s`;
3383
+ return `${(duration / 1e3).toFixed(0)} s`;
3384
+ }
3385
+ function withTimeout(promise, duration) {
3386
+ return new Promise((res, rej) => {
3387
+ const timeout = setTimeout(() => {
3388
+ rej(`Promise timed out after ${duration}ms`);
3389
+ }, duration);
3390
+ promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
3391
+ });
3392
+ }
3393
+
3394
+ // src/core/utils/network.ts
3395
+ function isOffline() {
3396
+ const isOffline2 = new Promise((res) => {
3397
+ import_node_dns.default.resolve("google.com", (err) => {
3398
+ if (err == null) {
3399
+ res(false);
3400
+ } else {
3401
+ res(true);
3402
+ }
3403
+ });
3404
+ });
3405
+ return withTimeout(isOffline2, 1e3).catch(() => true);
3594
3406
  }
3595
-
3596
- // src/core/utils/building/generate-wxt-dir.ts
3597
- async function generateTypesDir(entrypoints, config) {
3598
- await import_fs_extra6.default.ensureDir(config.typesDir);
3599
- const references = [];
3600
- const imports = getUnimportOptions(config);
3601
- if (imports !== false) {
3602
- references.push(await writeImportsDeclarationFile(config, imports));
3407
+ async function isOnline() {
3408
+ const offline = await isOffline();
3409
+ return !offline;
3410
+ }
3411
+ async function fetchCached(url2, config) {
3412
+ let content = "";
3413
+ if (await isOnline()) {
3414
+ const res = await fetch(url2);
3415
+ if (res.status < 300) {
3416
+ content = await res.text();
3417
+ await config.fsCache.set(url2, content);
3418
+ } else {
3419
+ config.logger.debug(
3420
+ `Failed to download "${url2}", falling back to cache...`
3421
+ );
3422
+ }
3603
3423
  }
3604
- references.push(await writePathsDeclarationFile(entrypoints, config));
3605
- references.push(await writeI18nDeclarationFile(config));
3606
- references.push(await writeGlobalsDeclarationFile(config));
3607
- const mainReference = await writeMainDeclarationFile(references, config);
3608
- await writeTsConfigFile(mainReference, config);
3424
+ if (!content)
3425
+ content = await config.fsCache.get(url2) ?? "";
3426
+ if (!content)
3427
+ throw Error(
3428
+ `Offline and "${url2}" has not been cached. Try again when online.`
3429
+ );
3430
+ return content;
3609
3431
  }
3610
- async function writeImportsDeclarationFile(config, unimportOptions) {
3611
- const filePath = (0, import_path6.resolve)(config.typesDir, "imports.d.ts");
3612
- const unimport2 = (0, import_unimport3.createUnimport)(unimportOptions);
3613
- await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
3614
- await writeFileIfDifferent(
3615
- filePath,
3616
- ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
3617
- "\n"
3618
- ) + "\n"
3619
- );
3620
- return filePath;
3432
+
3433
+ // src/core/builders/vite/plugins/download.ts
3434
+ function download(config) {
3435
+ return {
3436
+ name: "wxt:download",
3437
+ resolveId(id) {
3438
+ if (id.startsWith("url:"))
3439
+ return "\0" + id;
3440
+ },
3441
+ async load(id) {
3442
+ if (!id.startsWith("\0url:"))
3443
+ return;
3444
+ const url2 = id.replace("\0url:", "");
3445
+ return await fetchCached(url2, config);
3446
+ }
3447
+ };
3621
3448
  }
3622
- async function writePathsDeclarationFile(entrypoints, config) {
3623
- const filePath = (0, import_path6.resolve)(config.typesDir, "paths.d.ts");
3624
- const unions = entrypoints.map(
3625
- (entry) => getEntrypointBundlePath(
3626
- entry,
3627
- config.outDir,
3628
- entry.inputPath.endsWith(".html") ? ".html" : ".js"
3629
- )
3630
- ).concat(await getPublicFiles(config)).map(normalizePath).map((path11) => ` | "/${path11}"`).sort().join("\n");
3631
- const template = `// Generated by wxt
3632
- import "wxt/browser";
3633
3449
 
3634
- declare module "wxt/browser" {
3635
- export type PublicPath =
3636
- {{ union }}
3637
- export interface WxtRuntime extends Runtime.Static {
3638
- getURL(path: PublicPath): string;
3639
- }
3450
+ // src/core/builders/vite/plugins/multipageMove.ts
3451
+ var import_node_path5 = require("path");
3452
+ var import_fs_extra6 = __toESM(require("fs-extra"), 1);
3453
+ function multipageMove(entrypoints, config) {
3454
+ return {
3455
+ name: "wxt:multipage-move",
3456
+ async writeBundle(_, bundle) {
3457
+ for (const oldBundlePath in bundle) {
3458
+ const entrypoint = entrypoints.find(
3459
+ (entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath)
3460
+ );
3461
+ if (entrypoint == null) {
3462
+ config.logger.debug(
3463
+ `No entrypoint found for ${oldBundlePath}, leaving in chunks directory`
3464
+ );
3465
+ continue;
3466
+ }
3467
+ const newBundlePath = getEntrypointBundlePath(
3468
+ entrypoint,
3469
+ config.outDir,
3470
+ (0, import_node_path5.extname)(oldBundlePath)
3471
+ );
3472
+ if (newBundlePath === oldBundlePath) {
3473
+ config.logger.debug(
3474
+ "HTML file is already in the correct location",
3475
+ oldBundlePath
3476
+ );
3477
+ continue;
3478
+ }
3479
+ const oldAbsPath = (0, import_node_path5.resolve)(config.outDir, oldBundlePath);
3480
+ const newAbsPath = (0, import_node_path5.resolve)(config.outDir, newBundlePath);
3481
+ await (0, import_fs_extra6.ensureDir)((0, import_node_path5.dirname)(newAbsPath));
3482
+ await import_fs_extra6.default.move(oldAbsPath, newAbsPath, { overwrite: true });
3483
+ const renamedChunk = {
3484
+ ...bundle[oldBundlePath],
3485
+ fileName: newBundlePath
3486
+ };
3487
+ delete bundle[oldBundlePath];
3488
+ bundle[newBundlePath] = renamedChunk;
3489
+ }
3490
+ removeEmptyDirs(config.outDir);
3491
+ }
3492
+ };
3640
3493
  }
3641
- `;
3642
- await writeFileIfDifferent(
3643
- filePath,
3644
- template.replace("{{ union }}", unions || " | never")
3645
- );
3646
- return filePath;
3494
+ async function removeEmptyDirs(dir) {
3495
+ const files = await import_fs_extra6.default.readdir(dir);
3496
+ for (const file of files) {
3497
+ const filePath = (0, import_node_path5.join)(dir, file);
3498
+ const stats = await import_fs_extra6.default.stat(filePath);
3499
+ if (stats.isDirectory()) {
3500
+ await removeEmptyDirs(filePath);
3501
+ }
3502
+ }
3503
+ try {
3504
+ await import_fs_extra6.default.rmdir(dir);
3505
+ } catch {
3506
+ }
3647
3507
  }
3648
- async function writeI18nDeclarationFile(config) {
3649
- const filePath = (0, import_path6.resolve)(config.typesDir, "i18n.d.ts");
3650
- const defaultLocale = config.manifest.default_locale;
3651
- const template = `// Generated by wxt
3652
- import "wxt/browser";
3653
3508
 
3654
- declare module "wxt/browser" {
3655
- /**
3656
- * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
3657
- */
3658
- interface GetMessageOptions {
3659
- /**
3660
- * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
3661
- */
3662
- escapeLt?: boolean
3663
- }
3509
+ // src/core/builders/vite/plugins/unimport.ts
3510
+ var import_unimport3 = require("unimport");
3511
+ var import_path5 = require("path");
3512
+ var ENABLED_EXTENSIONS = /* @__PURE__ */ new Set([
3513
+ ".js",
3514
+ ".jsx",
3515
+ ".ts",
3516
+ ".tsx",
3517
+ ".vue",
3518
+ ".svelte"
3519
+ ]);
3520
+ function unimport(config) {
3521
+ const options = getUnimportOptions(config);
3522
+ if (options === false)
3523
+ return [];
3524
+ const unimport2 = (0, import_unimport3.createUnimport)(options);
3525
+ return {
3526
+ name: "wxt:unimport",
3527
+ async config() {
3528
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
3529
+ },
3530
+ async transform(code, id) {
3531
+ if (id.includes("node_modules"))
3532
+ return;
3533
+ if (!ENABLED_EXTENSIONS.has((0, import_path5.extname)(id)))
3534
+ return;
3535
+ return unimport2.injectImports(code, id);
3536
+ }
3537
+ };
3538
+ }
3664
3539
 
3665
- export interface WxtI18n extends I18n.Static {
3666
- {{ overrides }}
3667
- }
3540
+ // src/core/builders/vite/plugins/virtualEntrypoint.ts
3541
+ var import_fs_extra7 = __toESM(require("fs-extra"), 1);
3542
+ var import_path6 = require("path");
3543
+ function virtualEntrypoint(type, config) {
3544
+ const virtualId = `virtual:wxt-${type}?`;
3545
+ const resolvedVirtualId = `\0${virtualId}`;
3546
+ return {
3547
+ name: `wxt:virtual-entrypoint`,
3548
+ resolveId(id) {
3549
+ const index = id.indexOf(virtualId);
3550
+ if (index === -1)
3551
+ return;
3552
+ const inputPath = normalizePath(id.substring(index + virtualId.length));
3553
+ return resolvedVirtualId + inputPath;
3554
+ },
3555
+ async load(id) {
3556
+ if (!id.startsWith(resolvedVirtualId))
3557
+ return;
3558
+ const inputPath = id.replace(resolvedVirtualId, "");
3559
+ const template = await import_fs_extra7.default.readFile(
3560
+ (0, import_path6.resolve)(
3561
+ config.root,
3562
+ `node_modules/wxt/dist/virtual/${type}-entrypoint.js`
3563
+ ),
3564
+ "utf-8"
3565
+ );
3566
+ return template.replace(`virtual:user-${type}`, inputPath);
3567
+ }
3568
+ };
3668
3569
  }
3669
- `;
3670
- let messages;
3671
- if (defaultLocale) {
3672
- const defaultLocalePath = import_node_path6.default.resolve(
3673
- config.publicDir,
3674
- "_locales",
3675
- defaultLocale,
3676
- "messages.json"
3677
- );
3678
- const content = JSON.parse(await import_fs_extra6.default.readFile(defaultLocalePath, "utf-8"));
3679
- messages = parseI18nMessages(content);
3680
- } else {
3681
- messages = parseI18nMessages({});
3682
- }
3683
- const overrides = messages.map((message) => {
3684
- return ` /**
3685
- * ${message.description ?? "No message description."}
3686
- *
3687
- * "${message.message}"
3688
- */
3689
- getMessage(
3690
- messageName: "${message.name}",
3691
- substitutions?: string | string[],
3692
- options?: GetMessageOptions,
3693
- ): string;`;
3694
- });
3695
- await writeFileIfDifferent(
3696
- filePath,
3697
- template.replace("{{ overrides }}", overrides.join("\n"))
3698
- );
3699
- return filePath;
3570
+
3571
+ // src/core/builders/vite/plugins/tsconfigPaths.ts
3572
+ function tsconfigPaths(config) {
3573
+ return {
3574
+ name: "wxt:aliases",
3575
+ async config() {
3576
+ return {
3577
+ resolve: {
3578
+ alias: config.alias
3579
+ }
3580
+ };
3581
+ }
3582
+ };
3700
3583
  }
3701
- async function writeGlobalsDeclarationFile(config) {
3702
- const filePath = (0, import_path6.resolve)(config.typesDir, "globals.d.ts");
3703
- const globals2 = [...getGlobals(config), ...getEntrypointGlobals(config, "")];
3704
- await writeFileIfDifferent(
3705
- filePath,
3706
- [
3707
- "// Generated by wxt",
3708
- "export {}",
3709
- "declare global {",
3710
- ...globals2.map((global3) => ` const ${global3.name}: ${global3.type};`),
3711
- "}"
3712
- ].join("\n") + "\n"
3713
- );
3714
- return filePath;
3584
+
3585
+ // src/core/builders/vite/plugins/noopBackground.ts
3586
+ function noopBackground() {
3587
+ const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
3588
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
3589
+ return {
3590
+ name: "wxt:noop-background",
3591
+ resolveId(id) {
3592
+ if (id === virtualModuleId)
3593
+ return resolvedVirtualModuleId;
3594
+ },
3595
+ load(id) {
3596
+ if (id === resolvedVirtualModuleId) {
3597
+ return `import { defineBackground } from 'wxt/client';
3598
+ export default defineBackground(() => void 0)`;
3599
+ }
3600
+ }
3601
+ };
3715
3602
  }
3716
- async function writeMainDeclarationFile(references, config) {
3717
- const dir = config.wxtDir;
3718
- const filePath = (0, import_path6.resolve)(dir, "wxt.d.ts");
3719
- await writeFileIfDifferent(
3720
- filePath,
3721
- [
3722
- "// Generated by wxt",
3723
- `/// <reference types="vite/client" />`,
3724
- ...references.map(
3725
- (ref) => `/// <reference types="./${normalizePath((0, import_path6.relative)(dir, ref))}" />`
3726
- )
3727
- ].join("\n") + "\n"
3728
- );
3729
- return filePath;
3603
+
3604
+ // src/core/builders/vite/plugins/cssEntrypoints.ts
3605
+ function cssEntrypoints(entrypoint, config) {
3606
+ return {
3607
+ name: "wxt:css-entrypoint",
3608
+ config() {
3609
+ return {
3610
+ build: {
3611
+ rollupOptions: {
3612
+ output: {
3613
+ assetFileNames: () => getEntrypointBundlePath(entrypoint, config.outDir, ".css")
3614
+ }
3615
+ }
3616
+ }
3617
+ };
3618
+ },
3619
+ generateBundle(_, bundle) {
3620
+ Object.keys(bundle).forEach((file) => {
3621
+ if (file.endsWith(".js"))
3622
+ delete bundle[file];
3623
+ });
3624
+ }
3625
+ };
3730
3626
  }
3731
- async function writeTsConfigFile(mainReference, config) {
3732
- const dir = config.wxtDir;
3733
- const getTsconfigPath = (path11) => normalizePath((0, import_path6.relative)(dir, path11));
3734
- const paths = Object.entries(config.alias).flatMap(([alias, absolutePath]) => {
3735
- const aliasPath = getTsconfigPath(absolutePath);
3736
- return [
3737
- ` "${alias}": ["${aliasPath}"]`,
3738
- ` "${alias}/*": ["${aliasPath}/*"]`
3739
- ];
3740
- }).join(",\n");
3741
- await writeFileIfDifferent(
3742
- (0, import_path6.resolve)(dir, "tsconfig.json"),
3743
- `{
3744
- "compilerOptions": {
3745
- "target": "ESNext",
3746
- "module": "ESNext",
3747
- "moduleResolution": "Bundler",
3748
- "noEmit": true,
3749
- "esModuleInterop": true,
3750
- "forceConsistentCasingInFileNames": true,
3751
- "resolveJsonModule": true,
3752
- "strict": true,
3753
- "skipLibCheck": true,
3754
- "paths": {
3755
- ${paths}
3627
+
3628
+ // src/core/builders/vite/plugins/bundleAnalysis.ts
3629
+ var import_rollup_plugin_visualizer = require("rollup-plugin-visualizer");
3630
+ var increment = 0;
3631
+ function bundleAnalysis() {
3632
+ return (0, import_rollup_plugin_visualizer.visualizer)({
3633
+ emitFile: true,
3634
+ template: "raw-data",
3635
+ filename: `stats-${increment++}.json`
3636
+ });
3637
+ }
3638
+
3639
+ // src/core/builders/vite/plugins/globals.ts
3640
+ function globals(config) {
3641
+ return {
3642
+ name: "wxt:globals",
3643
+ config() {
3644
+ const define = {};
3645
+ for (const global3 of getGlobals(config)) {
3646
+ define[global3.name] = JSON.stringify(global3.value);
3647
+ }
3648
+ return {
3649
+ define
3650
+ };
3756
3651
  }
3757
- },
3758
- "include": [
3759
- "${getTsconfigPath(config.root)}/**/*",
3760
- "./${getTsconfigPath(mainReference)}"
3761
- ],
3762
- "exclude": ["${getTsconfigPath(config.outBaseDir)}"]
3763
- }`
3764
- );
3652
+ };
3765
3653
  }
3766
3654
 
3767
- // src/core/utils/building/get-internal-config.ts
3768
- var import_c12 = require("c12");
3769
- var import_node_path7 = __toESM(require("path"), 1);
3770
- var vite2 = __toESM(require("vite"), 1);
3655
+ // src/core/builders/vite/plugins/webextensionPolyfillAlias.ts
3656
+ var import_node_path6 = __toESM(require("path"), 1);
3771
3657
 
3772
- // src/core/utils/cache.ts
3773
- var import_fs_extra7 = __toESM(require("fs-extra"), 1);
3774
- var import_path7 = require("path");
3775
- function createFsCache(wxtDir) {
3776
- const getPath = (key) => (0, import_path7.resolve)(wxtDir, "cache", encodeURIComponent(key));
3658
+ // src/core/builders/vite/plugins/excludeBrowserPolyfill.ts
3659
+ function excludeBrowserPolyfill(config) {
3660
+ const virtualId = "virtual:wxt-webextension-polyfill-disabled";
3777
3661
  return {
3778
- async set(key, value) {
3779
- const path11 = getPath(key);
3780
- await (0, import_fs_extra7.ensureDir)((0, import_path7.dirname)(path11));
3781
- await writeFileIfDifferent(path11, value);
3662
+ name: "wxt:exclude-browser-polyfill",
3663
+ config() {
3664
+ if (config.experimental.includeBrowserPolyfill)
3665
+ return;
3666
+ return {
3667
+ resolve: {
3668
+ alias: {
3669
+ "webextension-polyfill": virtualId
3670
+ }
3671
+ }
3672
+ };
3782
3673
  },
3783
- async get(key) {
3784
- const path11 = getPath(key);
3785
- try {
3786
- return await import_fs_extra7.default.readFile(path11, "utf-8");
3787
- } catch {
3788
- return void 0;
3674
+ load(id) {
3675
+ if (id === virtualId) {
3676
+ return "export default chrome";
3677
+ }
3678
+ }
3679
+ };
3680
+ }
3681
+
3682
+ // src/core/builders/vite/plugins/entrypointGroupGlobals.ts
3683
+ function entrypointGroupGlobals(entrypointGroup) {
3684
+ return {
3685
+ name: "wxt:entrypoint-group-globals",
3686
+ config() {
3687
+ const define = {};
3688
+ let name = Array.isArray(entrypointGroup) ? "html" : entrypointGroup.name;
3689
+ for (const global3 of getEntrypointGlobals(name)) {
3690
+ define[global3.name] = JSON.stringify(global3.value);
3691
+ }
3692
+ return {
3693
+ define
3694
+ };
3695
+ }
3696
+ };
3697
+ }
3698
+
3699
+ // src/core/builders/vite/index.ts
3700
+ async function craeteViteBuilder(inlineConfig, userConfig, wxtConfig) {
3701
+ const vite = await import("vite");
3702
+ const getBaseConfig = async () => {
3703
+ const resolvedInlineConfig = await inlineConfig.vite?.(wxtConfig.env) ?? {};
3704
+ const resolvedUserConfig = await userConfig.vite?.(wxtConfig.env) ?? {};
3705
+ const config = vite.mergeConfig(
3706
+ resolvedUserConfig,
3707
+ resolvedInlineConfig
3708
+ );
3709
+ config.root = wxtConfig.root;
3710
+ config.configFile = false;
3711
+ config.logLevel = "warn";
3712
+ config.mode = wxtConfig.mode;
3713
+ config.build ??= {};
3714
+ config.build.outDir = wxtConfig.outDir;
3715
+ config.build.emptyOutDir = false;
3716
+ config.plugins ??= [];
3717
+ config.plugins.push(
3718
+ download(wxtConfig),
3719
+ devHtmlPrerender(wxtConfig),
3720
+ unimport(wxtConfig),
3721
+ virtualEntrypoint("background", wxtConfig),
3722
+ virtualEntrypoint("content-script", wxtConfig),
3723
+ virtualEntrypoint("unlisted-script", wxtConfig),
3724
+ devServerGlobals(wxtConfig),
3725
+ tsconfigPaths(wxtConfig),
3726
+ noopBackground(),
3727
+ globals(wxtConfig),
3728
+ excludeBrowserPolyfill(wxtConfig)
3729
+ );
3730
+ if (wxtConfig.analysis.enabled) {
3731
+ config.plugins.push(bundleAnalysis());
3732
+ }
3733
+ return config;
3734
+ };
3735
+ const getLibModeConfig = (entrypoint) => {
3736
+ const isVirtual = [
3737
+ "background",
3738
+ "content-script",
3739
+ "unlisted-script"
3740
+ ].includes(entrypoint.type);
3741
+ const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
3742
+ const plugins = [
3743
+ entrypointGroupGlobals(entrypoint)
3744
+ ];
3745
+ if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
3746
+ plugins.push(cssEntrypoints(entrypoint, wxtConfig));
3747
+ }
3748
+ const libMode = {
3749
+ mode: wxtConfig.mode,
3750
+ plugins,
3751
+ build: {
3752
+ lib: {
3753
+ entry,
3754
+ formats: ["iife"],
3755
+ name: "_",
3756
+ fileName: entrypoint.name
3757
+ },
3758
+ rollupOptions: {
3759
+ output: {
3760
+ // There's only a single output for this build, so we use the desired bundle path for the
3761
+ // entry output (like "content-scripts/overlay.js")
3762
+ entryFileNames: getEntrypointBundlePath(
3763
+ entrypoint,
3764
+ wxtConfig.outDir,
3765
+ ".js"
3766
+ ),
3767
+ // Output content script CSS to `content-scripts/`, but all other scripts are written to
3768
+ // `assets/`.
3769
+ assetFileNames: ({ name }) => {
3770
+ if (entrypoint.type === "content-script" && name?.endsWith("css")) {
3771
+ return `content-scripts/${entrypoint.name}.[ext]`;
3772
+ } else {
3773
+ return `assets/${entrypoint.name}.[ext]`;
3774
+ }
3775
+ }
3776
+ }
3777
+ }
3778
+ },
3779
+ define: {
3780
+ // See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
3781
+ "process.env.NODE_ENV": JSON.stringify(wxtConfig.mode)
3782
+ }
3783
+ };
3784
+ return libMode;
3785
+ };
3786
+ const getMultiPageConfig = (entrypoints) => {
3787
+ return {
3788
+ mode: wxtConfig.mode,
3789
+ plugins: [
3790
+ multipageMove(entrypoints, wxtConfig),
3791
+ entrypointGroupGlobals(entrypoints)
3792
+ ],
3793
+ build: {
3794
+ rollupOptions: {
3795
+ input: entrypoints.reduce((input, entry) => {
3796
+ input[entry.name] = entry.inputPath;
3797
+ return input;
3798
+ }, {}),
3799
+ output: {
3800
+ // Include a hash to prevent conflicts
3801
+ chunkFileNames: "chunks/[name]-[hash].js",
3802
+ // Include a hash to prevent conflicts
3803
+ entryFileNames: "chunks/[name]-[hash].js",
3804
+ // We can't control the "name", so we need a hash to prevent conflicts
3805
+ assetFileNames: "assets/[name]-[hash].[ext]"
3806
+ }
3807
+ }
3789
3808
  }
3809
+ };
3810
+ };
3811
+ const getCssConfig = (entrypoint) => {
3812
+ return {
3813
+ mode: wxtConfig.mode,
3814
+ plugins: [entrypointGroupGlobals(entrypoint)],
3815
+ build: {
3816
+ rollupOptions: {
3817
+ input: {
3818
+ [entrypoint.name]: entrypoint.inputPath
3819
+ },
3820
+ output: {
3821
+ assetFileNames: () => {
3822
+ if (entrypoint.type === "content-script-style") {
3823
+ return `content-scripts/${entrypoint.name}.[ext]`;
3824
+ } else {
3825
+ return `assets/${entrypoint.name}.[ext]`;
3826
+ }
3827
+ }
3828
+ }
3829
+ }
3830
+ }
3831
+ };
3832
+ };
3833
+ return {
3834
+ name: "Vite",
3835
+ version: vite.version,
3836
+ async build(group) {
3837
+ let entryConfig;
3838
+ if (Array.isArray(group))
3839
+ entryConfig = getMultiPageConfig(group);
3840
+ else if (group.inputPath.endsWith(".css"))
3841
+ entryConfig = getCssConfig(group);
3842
+ else
3843
+ entryConfig = getLibModeConfig(group);
3844
+ const buildConfig = vite.mergeConfig(await getBaseConfig(), entryConfig);
3845
+ const result = await vite.build(buildConfig);
3846
+ return {
3847
+ entrypoints: group,
3848
+ chunks: getBuildOutputChunks(result)
3849
+ };
3850
+ },
3851
+ async createServer(info) {
3852
+ const serverConfig = {
3853
+ server: {
3854
+ port: info.port,
3855
+ strictPort: true,
3856
+ host: info.hostname,
3857
+ origin: info.origin
3858
+ }
3859
+ };
3860
+ const baseConfig = await getBaseConfig();
3861
+ const viteServer = await vite.createServer(
3862
+ vite.mergeConfig(baseConfig, serverConfig)
3863
+ );
3864
+ const server = {
3865
+ async listen() {
3866
+ await viteServer.listen(info.port);
3867
+ },
3868
+ transformHtml(...args) {
3869
+ return viteServer.transformIndexHtml(...args);
3870
+ },
3871
+ ws: {
3872
+ send(message, payload) {
3873
+ return viteServer.ws.send(message, payload);
3874
+ },
3875
+ on(message, cb) {
3876
+ viteServer.ws.on(message, cb);
3877
+ }
3878
+ },
3879
+ watcher: viteServer.watcher
3880
+ };
3881
+ return server;
3790
3882
  }
3791
3883
  };
3792
3884
  }
3885
+ function getBuildOutputChunks(result) {
3886
+ if ("on" in result)
3887
+ throw Error("wxt does not support vite watch mode.");
3888
+ if (Array.isArray(result))
3889
+ return result.flatMap(({ output }) => output);
3890
+ return result.output;
3891
+ }
3793
3892
 
3794
3893
  // src/core/utils/building/get-internal-config.ts
3795
- var import_consola = __toESM(require("consola"), 1);
3796
- var import_defu = __toESM(require("defu"), 1);
3797
- async function getInternalConfig(inlineConfig, command) {
3894
+ var import_defu2 = __toESM(require("defu"), 1);
3895
+ async function getInternalConfig(inlineConfig, command, server) {
3798
3896
  let userConfig = {};
3799
3897
  let userConfigMetadata;
3800
3898
  if (inlineConfig.configFile !== false) {
@@ -3864,8 +3962,6 @@ async function getInternalConfig(inlineConfig, command) {
3864
3962
  runnerConfig,
3865
3963
  srcDir,
3866
3964
  typesDir,
3867
- vite: () => ({}),
3868
- // Real value added after this object is initialized.
3869
3965
  wxtDir,
3870
3966
  zip: resolveInternalZipConfig(root, mergedConfig),
3871
3967
  transformManifest(manifest) {
@@ -3880,10 +3976,18 @@ async function getInternalConfig(inlineConfig, command) {
3880
3976
  alias,
3881
3977
  experimental: {
3882
3978
  includeBrowserPolyfill: mergedConfig.experimental?.includeBrowserPolyfill ?? true
3883
- }
3979
+ },
3980
+ server
3981
+ };
3982
+ const builder = await craeteViteBuilder(
3983
+ inlineConfig,
3984
+ userConfig,
3985
+ finalConfig
3986
+ );
3987
+ return {
3988
+ ...finalConfig,
3989
+ builder
3884
3990
  };
3885
- finalConfig.vite = (env2) => resolveInternalViteConfig(env2, mergedConfig, finalConfig);
3886
- return finalConfig;
3887
3991
  }
3888
3992
  async function resolveManifestConfig(env, manifest) {
3889
3993
  return await (typeof manifest === "function" ? manifest(env) : manifest ?? {});
@@ -3895,26 +3999,18 @@ function mergeInlineConfig(inlineConfig, userConfig) {
3895
3999
  } else if (userConfig.imports == null && inlineConfig.imports == null) {
3896
4000
  imports = void 0;
3897
4001
  } else {
3898
- imports = vite2.mergeConfig(
3899
- userConfig.imports ?? {},
3900
- inlineConfig.imports ?? {}
3901
- );
4002
+ imports = (0, import_defu2.default)(inlineConfig.imports ?? {}, userConfig.imports ?? {});
3902
4003
  }
3903
4004
  const manifest = async (env) => {
3904
4005
  const user = await resolveManifestConfig(env, userConfig.manifest);
3905
4006
  const inline = await resolveManifestConfig(env, inlineConfig.manifest);
3906
- return vite2.mergeConfig(user, inline);
3907
- };
3908
- const viteConfig = async (env) => {
3909
- const user = await userConfig.vite?.(env);
3910
- const inline = await inlineConfig.vite?.(env);
3911
- return vite2.mergeConfig(user ?? {}, inline ?? {});
4007
+ return (0, import_defu2.default)(inline, user);
3912
4008
  };
3913
- const runner = (0, import_defu.default)(
4009
+ const runner = (0, import_defu2.default)(
3914
4010
  inlineConfig.runner ?? {},
3915
4011
  userConfig.runner ?? {}
3916
4012
  );
3917
- const zip2 = (0, import_defu.default)(
4013
+ const zip2 = (0, import_defu2.default)(
3918
4014
  inlineConfig.zip ?? {},
3919
4015
  userConfig.zip ?? {}
3920
4016
  );
@@ -3933,7 +4029,6 @@ function mergeInlineConfig(inlineConfig, userConfig) {
3933
4029
  runner,
3934
4030
  srcDir: inlineConfig.srcDir ?? userConfig.srcDir,
3935
4031
  outDir: inlineConfig.outDir ?? userConfig.outDir,
3936
- vite: viteConfig,
3937
4032
  zip: zip2,
3938
4033
  analysis: {
3939
4034
  enabled: inlineConfig.analysis?.enabled ?? userConfig.analysis?.enabled,
@@ -3947,6 +4042,7 @@ function mergeInlineConfig(inlineConfig, userConfig) {
3947
4042
  ...userConfig.experimental,
3948
4043
  ...inlineConfig.experimental
3949
4044
  },
4045
+ vite: void 0,
3950
4046
  transformManifest: void 0
3951
4047
  };
3952
4048
  }
@@ -3971,38 +4067,6 @@ function resolveInternalZipConfig(root, mergedConfig) {
3971
4067
  ]
3972
4068
  };
3973
4069
  }
3974
- async function resolveInternalViteConfig(env, mergedConfig, finalConfig) {
3975
- const internalVite = await mergedConfig.vite?.(env) ?? {};
3976
- internalVite.root = finalConfig.root;
3977
- internalVite.configFile = false;
3978
- internalVite.logLevel = "warn";
3979
- internalVite.mode = env.mode;
3980
- internalVite.build ??= {};
3981
- internalVite.build.outDir = finalConfig.outDir;
3982
- internalVite.build.emptyOutDir = false;
3983
- internalVite.plugins ??= [];
3984
- internalVite.plugins.push(download(finalConfig));
3985
- internalVite.plugins.push(devHtmlPrerender(finalConfig));
3986
- internalVite.plugins.push(unimport(finalConfig));
3987
- internalVite.plugins.push(
3988
- virtualEntrypoint("background", finalConfig)
3989
- );
3990
- internalVite.plugins.push(
3991
- virtualEntrypoint("content-script", finalConfig)
3992
- );
3993
- internalVite.plugins.push(
3994
- virtualEntrypoint("unlisted-script", finalConfig)
3995
- );
3996
- internalVite.plugins.push(devServerGlobals(finalConfig));
3997
- internalVite.plugins.push(tsconfigPaths(finalConfig));
3998
- internalVite.plugins.push(noopBackground());
3999
- if (finalConfig.analysis.enabled) {
4000
- internalVite.plugins.push(bundleAnalysis());
4001
- }
4002
- internalVite.plugins.push(globals(finalConfig));
4003
- internalVite.plugins.push(excludeBrowserPolyfill(finalConfig));
4004
- return internalVite;
4005
- }
4006
4070
 
4007
4071
  // src/core/utils/building/group-entrypoints.ts
4008
4072
  function groupEntrypoints(entrypoints) {
@@ -4044,7 +4108,7 @@ var ENTRY_TYPE_TO_GROUP_MAP = {
4044
4108
  var import_jiti = __toESM(require("jiti"), 1);
4045
4109
  var import_unimport5 = require("unimport");
4046
4110
  var import_fs_extra8 = __toESM(require("fs-extra"), 1);
4047
- var import_path8 = require("path");
4111
+ var import_path7 = require("path");
4048
4112
 
4049
4113
  // src/core/utils/strings.ts
4050
4114
  function kebabCaseAlphanumeric(str) {
@@ -4066,16 +4130,16 @@ ${noImports}`;
4066
4130
 
4067
4131
  // src/core/utils/building/import-entrypoint.ts
4068
4132
  var import_esbuild = require("esbuild");
4069
- async function importEntrypointFile(path11, config) {
4070
- config.logger.debug("Loading file metadata:", path11);
4071
- const normalPath = normalizePath(path11);
4133
+ async function importEntrypointFile(path10, config) {
4134
+ config.logger.debug("Loading file metadata:", path10);
4135
+ const normalPath = normalizePath(path10);
4072
4136
  const unimport2 = (0, import_unimport5.createUnimport)({
4073
4137
  ...getUnimportOptions(config),
4074
4138
  // Only allow specific imports, not all from the project
4075
4139
  dirs: []
4076
4140
  });
4077
4141
  await unimport2.init();
4078
- const text = await import_fs_extra8.default.readFile(path11, "utf-8");
4142
+ const text = await import_fs_extra8.default.readFile(path10, "utf-8");
4079
4143
  const textNoImports = removeProjectImportStatements(text);
4080
4144
  const { code } = await unimport2.injectImports(textNoImports);
4081
4145
  config.logger.debug(
@@ -4086,7 +4150,7 @@ async function importEntrypointFile(path11, config) {
4086
4150
  debug: config.debug,
4087
4151
  esmResolve: true,
4088
4152
  alias: {
4089
- "webextension-polyfill": (0, import_path8.resolve)(
4153
+ "webextension-polyfill": (0, import_path7.resolve)(
4090
4154
  config.root,
4091
4155
  "node_modules/wxt/dist/virtual/mock-browser.js"
4092
4156
  )
@@ -4106,7 +4170,7 @@ async function importEntrypointFile(path11, config) {
4106
4170
  }
4107
4171
  });
4108
4172
  try {
4109
- const res = await jiti(path11);
4173
+ const res = await jiti(path10);
4110
4174
  return res.default;
4111
4175
  } catch (err) {
4112
4176
  config.logger.error(err);
@@ -4124,11 +4188,10 @@ function getEsbuildOptions(opts) {
4124
4188
 
4125
4189
  // src/core/utils/building/internal-build.ts
4126
4190
  var import_picocolors4 = __toESM(require("picocolors"), 1);
4127
- var vite3 = __toESM(require("vite"), 1);
4128
4191
  var import_fs_extra12 = __toESM(require("fs-extra"), 1);
4129
4192
 
4130
4193
  // src/core/utils/log/printBuildSummary.ts
4131
- var import_path9 = require("path");
4194
+ var import_path8 = require("path");
4132
4195
 
4133
4196
  // src/core/utils/log/printFileList.ts
4134
4197
  var import_node_path8 = __toESM(require("path"), 1);
@@ -4215,7 +4278,7 @@ async function printBuildSummary(log, header, output, config) {
4215
4278
  return diff;
4216
4279
  return l.fileName.localeCompare(r.fileName);
4217
4280
  });
4218
- const files = chunks.map((chunk) => (0, import_path9.resolve)(config.outDir, chunk.fileName));
4281
+ const files = chunks.map((chunk) => (0, import_path8.resolve)(config.outDir, chunk.fileName));
4219
4282
  await printFileList(log, header, config.outDir, files);
4220
4283
  }
4221
4284
  var DEFAULT_SORT_WEIGHT = 100;
@@ -4236,7 +4299,7 @@ function getChunkSortWeight(filename) {
4236
4299
  var import_picocolors3 = __toESM(require("picocolors"), 1);
4237
4300
 
4238
4301
  // package.json
4239
- var version = "0.10.4";
4302
+ var version = "0.11.0";
4240
4303
 
4241
4304
  // src/core/utils/log/printHeader.ts
4242
4305
  var import_consola2 = require("consola");
@@ -4246,7 +4309,7 @@ var import_fast_glob3 = __toESM(require("fast-glob"), 1);
4246
4309
 
4247
4310
  // src/core/utils/manifest.ts
4248
4311
  var import_fs_extra11 = __toESM(require("fs-extra"), 1);
4249
- var import_path10 = require("path");
4312
+ var import_path9 = require("path");
4250
4313
 
4251
4314
  // src/core/utils/content-security-policy.ts
4252
4315
  var ContentSecurityPolicy = class _ContentSecurityPolicy {
@@ -4366,37 +4429,34 @@ async function getPackageJson(config) {
4366
4429
 
4367
4430
  // src/core/utils/manifest.ts
4368
4431
  var import_immer = require("immer");
4369
- var import_defu2 = __toESM(require("defu"), 1);
4432
+ var import_defu3 = __toESM(require("defu"), 1);
4370
4433
  async function writeManifest(manifest, output, config) {
4371
4434
  const str = config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
4372
4435
  await import_fs_extra11.default.ensureDir(config.outDir);
4373
- await writeFileIfDifferent((0, import_path10.resolve)(config.outDir, "manifest.json"), str);
4436
+ await writeFileIfDifferent((0, import_path9.resolve)(config.outDir, "manifest.json"), str);
4374
4437
  output.publicAssets.unshift({
4375
4438
  type: "asset",
4376
- fileName: "manifest.json",
4377
- name: "manifest",
4378
- needsCodeReference: false,
4379
- source: str
4439
+ fileName: "manifest.json"
4380
4440
  });
4381
4441
  }
4382
4442
  async function generateMainfest(entrypoints, buildOutput, config) {
4383
4443
  const pkg = await getPackageJson(config);
4384
4444
  const versionName = config.manifest.version_name ?? pkg?.version;
4385
- const version3 = config.manifest.version ?? simplifyVersion(pkg?.version);
4445
+ const version2 = config.manifest.version ?? simplifyVersion(pkg?.version);
4386
4446
  const baseManifest = {
4387
4447
  manifest_version: config.manifestVersion,
4388
4448
  name: pkg?.name,
4389
4449
  description: pkg?.description,
4390
- version: version3,
4450
+ version: version2,
4391
4451
  version_name: (
4392
4452
  // Firefox doesn't support version_name
4393
- config.browser === "firefox" || versionName === version3 ? void 0 : versionName
4453
+ config.browser === "firefox" || versionName === version2 ? void 0 : versionName
4394
4454
  ),
4395
4455
  short_name: pkg?.shortName,
4396
4456
  icons: discoverIcons(buildOutput)
4397
4457
  };
4398
4458
  const userManifest = config.manifest;
4399
- const manifest = (0, import_defu2.default)(
4459
+ const manifest = (0, import_defu3.default)(
4400
4460
  userManifest,
4401
4461
  baseManifest
4402
4462
  );
@@ -4418,14 +4478,14 @@ async function generateMainfest(entrypoints, buildOutput, config) {
4418
4478
  return finalManifest;
4419
4479
  }
4420
4480
  function simplifyVersion(versionName) {
4421
- const version3 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
4481
+ const version2 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
4422
4482
  versionName
4423
4483
  )?.[1];
4424
- if (version3 == null)
4484
+ if (version2 == null)
4425
4485
  throw Error(
4426
4486
  `Cannot simplify package.json version "${versionName}" to a valid extension version, "X.Y.Z"`
4427
4487
  );
4428
- return version3;
4488
+ return version2;
4429
4489
  }
4430
4490
  function addEntrypoints(manifest, entrypoints, buildOutput, config) {
4431
4491
  const entriesByType = entrypoints.reduce((map, entrypoint) => {
@@ -4797,7 +4857,7 @@ async function internalBuild(config) {
4797
4857
  const target = `${config.browser}-mv${config.manifestVersion}`;
4798
4858
  config.logger.info(
4799
4859
  `${verb} ${import_picocolors4.default.cyan(target)} for ${import_picocolors4.default.cyan(config.mode)} with ${import_picocolors4.default.green(
4800
- `Vite ${vite3.version}`
4860
+ `${config.builder.name} ${config.builder.version}`
4801
4861
  )}`
4802
4862
  );
4803
4863
  const startTime = Date.now();
@@ -4836,7 +4896,7 @@ async function combineAnalysisStats(config) {
4836
4896
  }
4837
4897
 
4838
4898
  // src/core/build.ts
4839
- async function build2(config) {
4899
+ async function build(config) {
4840
4900
  const internalConfig = await getInternalConfig(config ?? {}, "build");
4841
4901
  return await internalBuild(internalConfig);
4842
4902
  }
@@ -4886,9 +4946,6 @@ function defineRunnerConfig(config) {
4886
4946
  return config;
4887
4947
  }
4888
4948
 
4889
- // src/core/create-server.ts
4890
- var vite4 = __toESM(require("vite"), 1);
4891
-
4892
4949
  // src/core/runners/wsl.ts
4893
4950
  var import_node_path13 = require("path");
4894
4951
  function createWslRunner() {
@@ -5022,29 +5079,77 @@ var import_consola4 = require("consola");
5022
5079
  var import_async_mutex = require("async-mutex");
5023
5080
  var import_picocolors6 = __toESM(require("picocolors"), 1);
5024
5081
  var import_node_path16 = require("path");
5025
- async function createServer2(config) {
5026
- const serverInfo = await getServerInfo();
5027
- const getLatestInternalConfig = async () => {
5028
- return getInternalConfig(
5029
- {
5030
- ...config,
5031
- vite: () => serverInfo.viteServerConfig
5032
- },
5033
- "serve"
5034
- );
5082
+ async function createServer(inlineConfig) {
5083
+ const port = await getPort();
5084
+ const hostname = "localhost";
5085
+ const origin = `http://${hostname}:${port}`;
5086
+ const serverInfo = {
5087
+ port,
5088
+ hostname,
5089
+ origin
5035
5090
  };
5036
- let internalConfig = await getLatestInternalConfig();
5037
- const server = await setupServer(serverInfo, internalConfig);
5038
- internalConfig.server = server;
5039
- const fileChangedMutex = new import_async_mutex.Mutex();
5040
- const changeQueue = [];
5091
+ const server = {
5092
+ ...serverInfo,
5093
+ watcher: void 0,
5094
+ // Filled out later down below
5095
+ ws: void 0,
5096
+ // Filled out later down below
5097
+ currentOutput: void 0,
5098
+ // Filled out later down below
5099
+ async start() {
5100
+ await builderServer.listen();
5101
+ config.logger.success(`Started dev server @ ${serverInfo.origin}`);
5102
+ server.currentOutput = await internalBuild(config);
5103
+ await runner.openBrowser(config);
5104
+ },
5105
+ transformHtml(url2, html, originalUrl) {
5106
+ return builderServer.transformHtml(url2, html, originalUrl);
5107
+ },
5108
+ reloadContentScript(contentScript) {
5109
+ server.ws.send("wxt:reload-content-script", contentScript);
5110
+ },
5111
+ reloadPage(path10) {
5112
+ server.ws.send("wxt:reload-page", path10);
5113
+ },
5114
+ reloadExtension() {
5115
+ server.ws.send("wxt:reload-extension");
5116
+ }
5117
+ };
5118
+ const getLatestConfig = () => getInternalConfig(inlineConfig ?? {}, "serve", server);
5119
+ let config = await getLatestConfig();
5120
+ const [runner, builderServer] = await Promise.all([
5121
+ createExtensionRunner(config),
5122
+ config.builder.createServer(server)
5123
+ ]);
5124
+ server.watcher = builderServer.watcher;
5125
+ server.ws = builderServer.ws;
5041
5126
  server.ws.on("wxt:background-initialized", () => {
5042
- reloadContentScripts(server.currentOutput.steps, internalConfig, server);
5127
+ reloadContentScripts(server.currentOutput.steps, config, server);
5128
+ });
5129
+ const reloadOnChange = createFileReloader({
5130
+ server,
5131
+ getLatestConfig,
5132
+ updateConfig(newConfig) {
5133
+ config = newConfig;
5134
+ }
5043
5135
  });
5044
- server.watcher.on("all", async (event, path11, _stats) => {
5045
- if (path11.startsWith(internalConfig.outBaseDir))
5136
+ server.watcher.on("all", reloadOnChange);
5137
+ return server;
5138
+ }
5139
+ async function getPort() {
5140
+ const { default: getPort2, portNumbers } = await import("get-port");
5141
+ return await getPort2({ port: portNumbers(3e3, 3010) });
5142
+ }
5143
+ function createFileReloader(options) {
5144
+ const { server, getLatestConfig, updateConfig } = options;
5145
+ const fileChangedMutex = new import_async_mutex.Mutex();
5146
+ const changeQueue = [];
5147
+ return async (event, path10) => {
5148
+ const config = await getLatestConfig();
5149
+ updateConfig(config);
5150
+ if (path10.startsWith(config.outBaseDir))
5046
5151
  return;
5047
- changeQueue.push([event, path11]);
5152
+ changeQueue.push([event, path10]);
5048
5153
  await fileChangedMutex.runExclusive(async () => {
5049
5154
  const fileChanges = changeQueue.splice(0, changeQueue.length);
5050
5155
  if (fileChanges.length === 0)
@@ -5052,18 +5157,16 @@ async function createServer2(config) {
5052
5157
  const changes = detectDevChanges(fileChanges, server.currentOutput);
5053
5158
  if (changes.type === "no-change")
5054
5159
  return;
5055
- internalConfig.logger.info(
5056
- `Changed: ${Array.from(new Set(fileChanges.map((change) => change[1]))).map((file) => import_picocolors6.default.dim((0, import_node_path16.relative)(internalConfig.root, file))).join(", ")}`
5160
+ config.logger.info(
5161
+ `Changed: ${Array.from(new Set(fileChanges.map((change) => change[1]))).map((file) => import_picocolors6.default.dim((0, import_node_path16.relative)(config.root, file))).join(", ")}`
5057
5162
  );
5058
5163
  const rebuiltNames = changes.rebuildGroups.flat().map((entry) => {
5059
5164
  return import_picocolors6.default.cyan(
5060
- (0, import_node_path16.relative)(internalConfig.outDir, getEntrypointOutputFile(entry, ""))
5165
+ (0, import_node_path16.relative)(config.outDir, getEntrypointOutputFile(entry, ""))
5061
5166
  );
5062
5167
  }).join(import_picocolors6.default.dim(", "));
5063
- internalConfig = await getLatestInternalConfig();
5064
- internalConfig.server = server;
5065
5168
  const { output: newOutput } = await rebuild(
5066
- internalConfig,
5169
+ config,
5067
5170
  // TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
5068
5171
  changes.rebuildGroups,
5069
5172
  changes.cachedOutput
@@ -5074,74 +5177,15 @@ async function createServer2(config) {
5074
5177
  server.reloadExtension();
5075
5178
  break;
5076
5179
  case "html-reload":
5077
- reloadHtmlPages(changes.rebuildGroups, server, internalConfig);
5180
+ reloadHtmlPages(changes.rebuildGroups, server, config);
5078
5181
  break;
5079
5182
  case "content-script-reload":
5080
- reloadContentScripts(changes.changedSteps, internalConfig, server);
5183
+ reloadContentScripts(changes.changedSteps, config, server);
5081
5184
  break;
5082
5185
  }
5083
5186
  import_consola4.consola.success(`Reloaded: ${rebuiltNames}`);
5084
5187
  });
5085
- });
5086
- return server;
5087
- }
5088
- async function getServerInfo() {
5089
- const { default: getPort, portNumbers } = await import("get-port");
5090
- const port = await getPort({ port: portNumbers(3e3, 3010) });
5091
- const hostname = "localhost";
5092
- const origin = `http://${hostname}:${port}`;
5093
- const serverConfig = {
5094
- server: {
5095
- origin
5096
- }
5097
- };
5098
- return {
5099
- port,
5100
- hostname,
5101
- origin,
5102
- viteServerConfig: serverConfig
5103
- };
5104
- }
5105
- async function setupServer(serverInfo, config) {
5106
- const runner = await createExtensionRunner(config);
5107
- const viteServer = await vite4.createServer(
5108
- vite4.mergeConfig(serverInfo, await config.vite(config.env))
5109
- );
5110
- const start = async () => {
5111
- await viteServer.listen(server.port);
5112
- config.logger.success(`Started dev server @ ${serverInfo.origin}`);
5113
- server.currentOutput = await internalBuild(config);
5114
- await runner.openBrowser(config);
5115
5188
  };
5116
- const reloadExtension = () => {
5117
- viteServer.ws.send("wxt:reload-extension");
5118
- };
5119
- const reloadPage = (path11) => {
5120
- viteServer.ws.send("wxt:reload-page", path11);
5121
- };
5122
- const reloadContentScript = (contentScript) => {
5123
- viteServer.ws.send("wxt:reload-content-script", contentScript);
5124
- };
5125
- const server = {
5126
- ...viteServer,
5127
- start,
5128
- currentOutput: {
5129
- manifest: {
5130
- manifest_version: 3,
5131
- name: "",
5132
- version: ""
5133
- },
5134
- publicAssets: [],
5135
- steps: []
5136
- },
5137
- port: serverInfo.port,
5138
- hostname: serverInfo.hostname,
5139
- origin: serverInfo.origin,
5140
- reloadExtension,
5141
- reloadPage,
5142
- reloadContentScript
5143
- };
5144
- return server;
5145
5189
  }
5146
5190
  function reloadContentScripts(steps, config, server) {
5147
5191
  if (config.manifestVersion === 3) {
@@ -5175,8 +5219,8 @@ function reloadContentScripts(steps, config, server) {
5175
5219
  }
5176
5220
  function reloadHtmlPages(groups, server, config) {
5177
5221
  groups.flat().forEach((entry) => {
5178
- const path11 = getEntrypointBundlePath(entry, config.outDir, ".html");
5179
- server.reloadPage(path11);
5222
+ const path10 = getEntrypointBundlePath(entry, config.outDir, ".html");
5223
+ server.reloadPage(path10);
5180
5224
  });
5181
5225
  }
5182
5226
 
@@ -5358,8 +5402,8 @@ async function zip(config) {
5358
5402
  );
5359
5403
  await (0, import_zip_dir.default)(internalConfig.zip.sourcesRoot, {
5360
5404
  saveTo: sourcesZipPath,
5361
- filter(path11) {
5362
- const relativePath = (0, import_node_path18.relative)(internalConfig.zip.sourcesRoot, path11);
5405
+ filter(path10) {
5406
+ const relativePath = (0, import_node_path18.relative)(internalConfig.zip.sourcesRoot, path10);
5363
5407
  const matchedPattern = internalConfig.zip.ignoredSources.find(
5364
5408
  (pattern) => (0, import_minimatch2.minimatch)(relativePath, pattern)
5365
5409
  );