wxt 0.8.4 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +3 -2
  2. package/dist/browser.js +3 -4
  3. package/dist/chunk-FNTE2L27.js +7 -0
  4. package/dist/chunk-VFZ5667B.js +2410 -0
  5. package/dist/chunk-YUG22S6W.js +38 -0
  6. package/dist/cli.cjs +3941 -3775
  7. package/dist/cli.d.cts +2 -0
  8. package/dist/client.d.ts +5 -171
  9. package/dist/client.js +128 -131
  10. package/dist/execa-WKZHVHC5.js +2043 -0
  11. package/dist/external-9107db91.d.ts +176 -0
  12. package/dist/external-cb0967d6.d.ts +572 -0
  13. package/dist/index.cjs +2727 -2355
  14. package/dist/index.d.cts +36 -583
  15. package/dist/index.d.ts +36 -583
  16. package/dist/index.js +330 -4484
  17. package/dist/sandbox.d.ts +2 -23
  18. package/dist/sandbox.js +1 -2
  19. package/dist/testing.cjs +161 -35
  20. package/dist/testing.d.cts +7 -273
  21. package/dist/testing.d.ts +7 -273
  22. package/dist/testing.js +12 -676
  23. package/dist/{virtual-modules → virtual}/background-entrypoint.js +6 -7
  24. package/dist/{virtual-modules → virtual}/content-script-entrypoint.js +4 -5
  25. package/dist/virtual/mock-browser.js +152 -0
  26. package/dist/{virtual-modules → virtual}/reload-html.js +2 -3
  27. package/dist/{virtual-modules → virtual}/unlisted-script-entrypoint.js +2 -3
  28. package/package.json +6 -5
  29. package/dist/index.cjs.map +0 -1
  30. package/dist/index.js.map +0 -1
  31. package/dist/virtual-modules/background-entrypoint.js.map +0 -1
  32. package/dist/virtual-modules/content-script-entrypoint.js.map +0 -1
  33. package/dist/virtual-modules/fake-browser.cjs +0 -31
  34. package/dist/virtual-modules/fake-browser.js +0 -8
  35. package/dist/virtual-modules/reload-html.js.map +0 -1
  36. package/dist/virtual-modules/unlisted-script-entrypoint.js.map +0 -1
@@ -0,0 +1,2410 @@
1
+ // package.json
2
+ var version = "0.8.6";
3
+
4
+ // src/core/utils/entrypoints.ts
5
+ import path, { relative, resolve } from "node:path";
6
+
7
+ // src/core/utils/paths.ts
8
+ import nodePath from "node:path";
9
+ import * as vite from "vite";
10
+ function normalizePath2(path7) {
11
+ return vite.normalizePath(path7);
12
+ }
13
+ function unnormalizePath(path7) {
14
+ return nodePath.normalize(path7);
15
+ }
16
+ var CSS_EXTENSIONS = ["css", "scss", "sass", "less", "styl", "stylus"];
17
+ var CSS_EXTENSIONS_PATTERN = `+(${CSS_EXTENSIONS.join("|")})`;
18
+
19
+ // src/core/utils/entrypoints.ts
20
+ function getEntrypointName(entrypointsDir, inputPath) {
21
+ const relativePath = path.relative(entrypointsDir, inputPath);
22
+ const name = relativePath.split(/[\.\/\\]/, 2)[0];
23
+ return name;
24
+ }
25
+ function getEntrypointOutputFile(entrypoint, ext) {
26
+ return resolve(entrypoint.outputDir, `${entrypoint.name}${ext}`);
27
+ }
28
+ function getEntrypointBundlePath(entrypoint, outDir, ext) {
29
+ return normalizePath2(
30
+ relative(outDir, getEntrypointOutputFile(entrypoint, ext))
31
+ );
32
+ }
33
+ function resolvePerBrowserOption(option, browser) {
34
+ if (typeof option === "object" && !Array.isArray(option))
35
+ return option[browser];
36
+ return option;
37
+ }
38
+
39
+ // src/core/utils/time.ts
40
+ function formatDuration(duration) {
41
+ if (duration < 1e3)
42
+ return `${duration} ms`;
43
+ if (duration < 1e4)
44
+ return `${(duration / 1e3).toFixed(3)} s`;
45
+ if (duration < 6e4)
46
+ return `${(duration / 1e3).toFixed(1)} s`;
47
+ return `${(duration / 1e3).toFixed(0)} s`;
48
+ }
49
+ function withTimeout(promise, duration) {
50
+ return new Promise((res, rej) => {
51
+ const timeout = setTimeout(() => {
52
+ rej(`Promise timed out after ${duration}ms`);
53
+ }, duration);
54
+ promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
55
+ });
56
+ }
57
+
58
+ // src/core/utils/network.ts
59
+ import dns from "node:dns";
60
+ function isOffline() {
61
+ const isOffline2 = new Promise((res) => {
62
+ dns.resolve("google.com", (err) => {
63
+ if (err == null) {
64
+ res(false);
65
+ } else {
66
+ res(true);
67
+ }
68
+ });
69
+ });
70
+ return withTimeout(isOffline2, 1e3).catch(() => true);
71
+ }
72
+ async function isOnline() {
73
+ const offline = await isOffline();
74
+ return !offline;
75
+ }
76
+ async function fetchCached(url, config) {
77
+ let content = "";
78
+ if (await isOnline()) {
79
+ const res = await fetch(url);
80
+ if (res.status < 300) {
81
+ content = await res.text();
82
+ await config.fsCache.set(url, content);
83
+ } else {
84
+ config.logger.debug(
85
+ `Failed to download "${url}", falling back to cache...`
86
+ );
87
+ }
88
+ }
89
+ if (!content)
90
+ content = await config.fsCache.get(url) ?? "";
91
+ if (!content)
92
+ throw Error(
93
+ `Offline and "${url}" has not been cached. Try again when online.`
94
+ );
95
+ return content;
96
+ }
97
+
98
+ // src/core/vite-plugins/download.ts
99
+ function download(config) {
100
+ return {
101
+ name: "wxt:download",
102
+ resolveId(id) {
103
+ if (id.startsWith("url:"))
104
+ return "\0" + id;
105
+ },
106
+ async load(id) {
107
+ if (!id.startsWith("\0url:"))
108
+ return;
109
+ const url = id.replace("\0url:", "");
110
+ return await fetchCached(url, config);
111
+ }
112
+ };
113
+ }
114
+
115
+ // src/core/vite-plugins/unimport.ts
116
+ import { createUnimport } from "unimport";
117
+
118
+ // src/core/utils/unimport.ts
119
+ import { mergeConfig } from "vite";
120
+ function getUnimportOptions(config) {
121
+ if (config.imports === false)
122
+ return false;
123
+ const defaultOptions = {
124
+ debugLog: config.logger.debug,
125
+ imports: [
126
+ { name: "defineConfig", from: "wxt" },
127
+ { name: "fakeBrowser", from: "wxt/testing" }
128
+ ],
129
+ presets: [
130
+ { package: "wxt/client" },
131
+ { package: "wxt/browser" },
132
+ { package: "wxt/sandbox" }
133
+ ],
134
+ warn: config.logger.warn,
135
+ dirs: ["components", "composables", "hooks", "utils"]
136
+ };
137
+ return mergeConfig(
138
+ defaultOptions,
139
+ config.imports
140
+ );
141
+ }
142
+
143
+ // src/core/vite-plugins/unimport.ts
144
+ import { extname } from "path";
145
+ var ENABLED_EXTENSIONS = {
146
+ ".js": true,
147
+ ".jsx": true,
148
+ ".ts": true,
149
+ ".tsx": true,
150
+ ".vue": true,
151
+ ".svelte": true
152
+ };
153
+ function unimport(config) {
154
+ const options = getUnimportOptions(config);
155
+ if (options === false)
156
+ return [];
157
+ const unimport2 = createUnimport(options);
158
+ return {
159
+ name: "wxt:unimport",
160
+ async config() {
161
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
162
+ },
163
+ async transform(code, id) {
164
+ const ext = extname(id);
165
+ if (ENABLED_EXTENSIONS[ext])
166
+ return unimport2.injectImports(code, id);
167
+ }
168
+ };
169
+ }
170
+
171
+ // src/core/vite-plugins/tsconfigPaths.ts
172
+ function tsconfigPaths(config) {
173
+ return {
174
+ name: "wxt:aliases",
175
+ async config() {
176
+ return {
177
+ resolve: {
178
+ alias: {
179
+ "@@": config.root,
180
+ "~~": config.root,
181
+ "@": config.srcDir,
182
+ "~": config.srcDir
183
+ }
184
+ }
185
+ };
186
+ }
187
+ };
188
+ }
189
+
190
+ // src/core/utils/globals.ts
191
+ function getGlobals(config) {
192
+ return [
193
+ {
194
+ name: surroundInUnderscore("MANIFEST_VERSION"),
195
+ value: config.manifestVersion,
196
+ type: `2 | 3`
197
+ },
198
+ {
199
+ name: surroundInUnderscore("BROWSER"),
200
+ value: config.browser,
201
+ type: `string`
202
+ },
203
+ {
204
+ name: surroundInUnderscore("IS_CHROME"),
205
+ value: config.browser === "chrome",
206
+ type: `boolean`
207
+ },
208
+ {
209
+ name: surroundInUnderscore("IS_FIREFOX"),
210
+ value: config.browser === "firefox",
211
+ type: `boolean`
212
+ },
213
+ {
214
+ name: surroundInUnderscore("IS_SAFARI"),
215
+ value: config.browser === "safari",
216
+ type: `boolean`
217
+ },
218
+ {
219
+ name: surroundInUnderscore("IS_EDGE"),
220
+ value: config.browser === "edge",
221
+ type: `boolean`
222
+ },
223
+ {
224
+ name: surroundInUnderscore("IS_OPERA"),
225
+ value: config.browser === "opera",
226
+ type: `boolean`
227
+ },
228
+ {
229
+ name: surroundInUnderscore("COMMAND"),
230
+ value: config.command,
231
+ type: `"build" | "serve"`
232
+ }
233
+ ];
234
+ }
235
+ function getEntrypointGlobals(config, entrypointName) {
236
+ return [
237
+ {
238
+ name: surroundInUnderscore("ENTRYPOINT"),
239
+ value: entrypointName,
240
+ type: `string`
241
+ }
242
+ ];
243
+ }
244
+ function surroundInUnderscore(name) {
245
+ return `__${name}__`;
246
+ }
247
+
248
+ // src/core/vite-plugins/globals.ts
249
+ function globals(config) {
250
+ return {
251
+ name: "wxt:globals",
252
+ config() {
253
+ const define = {};
254
+ for (const global of getGlobals(config)) {
255
+ define[global.name] = JSON.stringify(global.value);
256
+ }
257
+ return {
258
+ define
259
+ };
260
+ }
261
+ };
262
+ }
263
+
264
+ // src/core/vite-plugins/webextensionPolyfillAlias.ts
265
+ import path2 from "node:path";
266
+ function webextensionPolyfillAlias(config) {
267
+ return {
268
+ name: "wxt:webextension-polyfill-test-alias",
269
+ config() {
270
+ return {
271
+ resolve: {
272
+ alias: {
273
+ "webextension-polyfill": path2.resolve(
274
+ config.root,
275
+ "node_modules/wxt/dist/virtual/mock-browser"
276
+ )
277
+ }
278
+ }
279
+ };
280
+ }
281
+ };
282
+ }
283
+
284
+ // src/core/vite-plugins/webextensionPolyfillInlineDeps.ts
285
+ function webextensionPolyfillInlineDeps() {
286
+ return {
287
+ name: "wxt:testing-inline-deps",
288
+ config() {
289
+ const wxtModules = ["wxt/browser", "wxt/client"];
290
+ return {
291
+ test: {
292
+ server: {
293
+ deps: {
294
+ inline: [...wxtModules]
295
+ }
296
+ }
297
+ }
298
+ };
299
+ }
300
+ };
301
+ }
302
+
303
+ // src/core/vite-plugins/devHtmlPrerender.ts
304
+ import { parseHTML } from "linkedom";
305
+ import { dirname, isAbsolute, relative as relative2, resolve as resolve2 } from "path";
306
+ var reactRefreshPreamble = "";
307
+ function devHtmlPrerender(config) {
308
+ const htmlReloadId = "@wxt/reload-html";
309
+ const resolvedHtmlReloadId = resolve2(
310
+ config.root,
311
+ "node_modules/wxt/dist/virtual/reload-html.js"
312
+ );
313
+ const virtualReactRefreshId = "@wxt/virtual-react-refresh";
314
+ const resolvedVirtualReactRefreshId = "\0" + virtualReactRefreshId;
315
+ return [
316
+ {
317
+ apply: "build",
318
+ name: "wxt:dev-html-prerender",
319
+ config() {
320
+ return {
321
+ resolve: {
322
+ alias: {
323
+ [htmlReloadId]: resolvedHtmlReloadId
324
+ }
325
+ }
326
+ };
327
+ },
328
+ // Convert scripts like src="./main.tsx" -> src="http://localhost:3000/entrypoints/popup/main.tsx"
329
+ // before the paths are replaced with their bundled path
330
+ transform(code, id) {
331
+ const server = config.server;
332
+ if (config.command !== "serve" || server == null || !id.endsWith(".html"))
333
+ return;
334
+ const { document } = parseHTML(code);
335
+ const pointToDevServer = (querySelector, attr) => {
336
+ document.querySelectorAll(querySelector).forEach((element) => {
337
+ const src = element.getAttribute(attr);
338
+ if (!src)
339
+ return;
340
+ if (isAbsolute(src)) {
341
+ element.setAttribute(attr, server.origin + src);
342
+ } else if (src.startsWith(".")) {
343
+ const abs = resolve2(dirname(id), src);
344
+ const pathname = relative2(config.root, abs);
345
+ element.setAttribute(attr, `${server.origin}/${pathname}`);
346
+ }
347
+ });
348
+ };
349
+ pointToDevServer("script[type=module]", "src");
350
+ pointToDevServer("link[rel=stylesheet]", "href");
351
+ const reloader = document.createElement("script");
352
+ reloader.src = htmlReloadId;
353
+ reloader.type = "module";
354
+ document.head.appendChild(reloader);
355
+ const newHtml = document.toString();
356
+ config.logger.debug("transform " + id);
357
+ config.logger.debug("Old HTML:\n" + code);
358
+ config.logger.debug("New HTML:\n" + newHtml);
359
+ return newHtml;
360
+ },
361
+ // Pass the HTML through the dev server to add dev-mode specific code
362
+ async transformIndexHtml(html, ctx) {
363
+ const server = config.server;
364
+ if (config.command !== "serve" || server == null)
365
+ return;
366
+ const originalUrl = `${server.origin}${ctx.path}`;
367
+ const name = getEntrypointName(config.entrypointsDir, ctx.filename);
368
+ const url = `${server.origin}/${name}.html`;
369
+ const serverHtml = await server.transformIndexHtml(
370
+ url,
371
+ html,
372
+ originalUrl
373
+ );
374
+ const { document } = parseHTML(serverHtml);
375
+ const reactRefreshScript = Array.from(
376
+ document.querySelectorAll("script[type=module]")
377
+ ).find((script) => script.innerHTML.includes("@react-refresh"));
378
+ if (reactRefreshScript) {
379
+ reactRefreshPreamble = reactRefreshScript.innerHTML;
380
+ const virtualScript = document.createElement("script");
381
+ virtualScript.type = "module";
382
+ virtualScript.src = `${server.origin}/${virtualReactRefreshId}`;
383
+ reactRefreshScript.replaceWith(virtualScript);
384
+ }
385
+ const viteClientScript = document.querySelector(
386
+ "script[src='/@vite/client']"
387
+ );
388
+ if (viteClientScript) {
389
+ viteClientScript.src = `${server.origin}${viteClientScript.src}`;
390
+ }
391
+ const newHtml = document.toString();
392
+ config.logger.debug("transformIndexHtml " + ctx.filename);
393
+ config.logger.debug("Old HTML:\n" + html);
394
+ config.logger.debug("New HTML:\n" + newHtml);
395
+ return newHtml;
396
+ }
397
+ },
398
+ {
399
+ name: "wxt:virtualize-react-refresh",
400
+ apply: "serve",
401
+ resolveId(id) {
402
+ if (id === `/${virtualReactRefreshId}`) {
403
+ return resolvedVirtualReactRefreshId;
404
+ }
405
+ if (id.startsWith("/chunks/")) {
406
+ return "\0noop";
407
+ }
408
+ },
409
+ load(id) {
410
+ if (id === resolvedVirtualReactRefreshId) {
411
+ return reactRefreshPreamble;
412
+ }
413
+ if (id === "\0noop") {
414
+ return "";
415
+ }
416
+ }
417
+ }
418
+ ];
419
+ }
420
+
421
+ // src/core/vite-plugins/devServerGlobals.ts
422
+ function devServerGlobals(internalConfig) {
423
+ return {
424
+ name: "wxt:dev-server-globals",
425
+ config() {
426
+ if (internalConfig.server == null || internalConfig.command == "build")
427
+ return;
428
+ return {
429
+ define: {
430
+ __DEV_SERVER_PROTOCOL__: JSON.stringify("ws:"),
431
+ __DEV_SERVER_HOSTNAME__: JSON.stringify(
432
+ internalConfig.server.hostname
433
+ ),
434
+ __DEV_SERVER_PORT__: JSON.stringify(internalConfig.server.port)
435
+ }
436
+ };
437
+ }
438
+ };
439
+ }
440
+
441
+ // src/core/vite-plugins/multipageMove.ts
442
+ import { dirname as dirname2, extname as extname2, resolve as resolve3 } from "node:path";
443
+ import fs, { ensureDir } from "fs-extra";
444
+ function multipageMove(entrypoints, config) {
445
+ return {
446
+ name: "wxt:multipage-move",
447
+ async writeBundle(_, bundle) {
448
+ for (const oldBundlePath in bundle) {
449
+ const entrypoint = entrypoints.find(
450
+ (entry) => !!normalizePath2(entry.inputPath).endsWith(oldBundlePath)
451
+ );
452
+ if (entrypoint == null) {
453
+ config.logger.debug(
454
+ `No entrypoint found for ${oldBundlePath}, leaving in chunks directory`
455
+ );
456
+ continue;
457
+ }
458
+ const newBundlePath = getEntrypointBundlePath(
459
+ entrypoint,
460
+ config.outDir,
461
+ extname2(oldBundlePath)
462
+ );
463
+ if (newBundlePath === oldBundlePath) {
464
+ config.logger.debug(
465
+ "HTML file is already in the correct location",
466
+ oldBundlePath
467
+ );
468
+ continue;
469
+ }
470
+ const oldAbsPath = resolve3(config.outDir, oldBundlePath);
471
+ const newAbsPath = resolve3(config.outDir, newBundlePath);
472
+ await ensureDir(dirname2(newAbsPath));
473
+ await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
474
+ const renamedChunk = {
475
+ ...bundle[oldBundlePath],
476
+ fileName: newBundlePath
477
+ };
478
+ delete bundle[oldBundlePath];
479
+ bundle[newBundlePath] = renamedChunk;
480
+ }
481
+ }
482
+ };
483
+ }
484
+
485
+ // src/core/vite-plugins/virtualEntrypoint.ts
486
+ import fs2 from "fs-extra";
487
+ import { resolve as resolve4 } from "path";
488
+ function virtualEntrypoint(type, config) {
489
+ const virtualId = `virtual:wxt-${type}?`;
490
+ const resolvedVirtualId = `\0${virtualId}`;
491
+ return {
492
+ name: `wxt:virtual-entrypoint`,
493
+ resolveId(id) {
494
+ const index = id.indexOf(virtualId);
495
+ if (index === -1)
496
+ return;
497
+ const inputPath = normalizePath2(id.substring(index + virtualId.length));
498
+ return resolvedVirtualId + inputPath;
499
+ },
500
+ async load(id) {
501
+ if (!id.startsWith(resolvedVirtualId))
502
+ return;
503
+ const inputPath = id.replace(resolvedVirtualId, "");
504
+ const template = await fs2.readFile(
505
+ resolve4(
506
+ config.root,
507
+ `node_modules/wxt/dist/virtual/${type}-entrypoint.js`
508
+ ),
509
+ "utf-8"
510
+ );
511
+ return template.replace(`virtual:user-${type}`, inputPath);
512
+ }
513
+ };
514
+ }
515
+
516
+ // src/core/vite-plugins/noopBackground.ts
517
+ function noopBackground() {
518
+ const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
519
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
520
+ return {
521
+ name: "wxt:noop-background",
522
+ resolveId(id) {
523
+ if (id === virtualModuleId)
524
+ return resolvedVirtualModuleId;
525
+ },
526
+ load(id) {
527
+ if (id === resolvedVirtualModuleId) {
528
+ return `import { defineBackground } from 'wxt/client';
529
+ export default defineBackground(() => void 0)`;
530
+ }
531
+ }
532
+ };
533
+ }
534
+ var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
535
+
536
+ // src/core/vite-plugins/cssEntrypoints.ts
537
+ function cssEntrypoints(entrypoint, config) {
538
+ return {
539
+ name: "wxt:css-entrypoint",
540
+ config() {
541
+ return {
542
+ build: {
543
+ rollupOptions: {
544
+ output: {
545
+ assetFileNames: () => getEntrypointBundlePath(entrypoint, config.outDir, ".css")
546
+ }
547
+ }
548
+ }
549
+ };
550
+ },
551
+ generateBundle(_, bundle) {
552
+ Object.keys(bundle).forEach((file) => {
553
+ if (file.endsWith(".js"))
554
+ delete bundle[file];
555
+ });
556
+ }
557
+ };
558
+ }
559
+
560
+ // src/core/vite-plugins/bundleAnalysis.ts
561
+ import { visualizer } from "rollup-plugin-visualizer";
562
+ var increment = 0;
563
+ function bundleAnalysis() {
564
+ return visualizer({
565
+ emitFile: true,
566
+ template: "raw-data",
567
+ filename: `stats-${increment++}.json`
568
+ });
569
+ }
570
+
571
+ // src/core/utils/arrays.ts
572
+ function every(array, predicate) {
573
+ for (let i = 0; i < array.length; i++)
574
+ if (!predicate(array[i], i))
575
+ return false;
576
+ return true;
577
+ }
578
+
579
+ // src/core/utils/building/detect-dev-changes.ts
580
+ function detectDevChanges(changedFiles, currentOutput) {
581
+ if (currentOutput == null)
582
+ return { type: "no-change" };
583
+ const changedSteps = new Set(
584
+ changedFiles.flatMap(
585
+ (changedFile) => findEffectedSteps(changedFile, currentOutput)
586
+ )
587
+ );
588
+ if (changedSteps.size === 0)
589
+ return { type: "no-change" };
590
+ const unchangedOutput = {
591
+ manifest: currentOutput.manifest,
592
+ steps: [],
593
+ publicAssets: []
594
+ };
595
+ const changedOutput = {
596
+ manifest: currentOutput.manifest,
597
+ steps: [],
598
+ publicAssets: []
599
+ };
600
+ for (const step of currentOutput.steps) {
601
+ if (changedSteps.has(step)) {
602
+ changedOutput.steps.push(step);
603
+ } else {
604
+ unchangedOutput.steps.push(step);
605
+ }
606
+ }
607
+ for (const asset of currentOutput.publicAssets) {
608
+ if (changedSteps.has(asset)) {
609
+ changedOutput.publicAssets.push(asset);
610
+ } else {
611
+ unchangedOutput.publicAssets.push(asset);
612
+ }
613
+ }
614
+ const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, ([_, file]) => file.endsWith(".html"));
615
+ if (isOnlyHtmlChanges) {
616
+ return {
617
+ type: "html-reload",
618
+ cachedOutput: unchangedOutput,
619
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
620
+ };
621
+ }
622
+ const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
623
+ changedOutput.steps.flatMap((step) => step.entrypoints),
624
+ (entry) => entry.type === "content-script"
625
+ );
626
+ if (isOnlyContentScripts) {
627
+ return {
628
+ type: "content-script-reload",
629
+ cachedOutput: unchangedOutput,
630
+ changedSteps: changedOutput.steps,
631
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
632
+ };
633
+ }
634
+ return {
635
+ type: "extension-reload",
636
+ cachedOutput: unchangedOutput,
637
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
638
+ };
639
+ }
640
+ function findEffectedSteps(changedFile, currentOutput) {
641
+ const changes = [];
642
+ const changedPath = normalizePath2(changedFile[1]);
643
+ const isChunkEffected = (chunk) => (
644
+ // If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
645
+ // fileName is normalized, relative bundle path
646
+ chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
647
+ // moduleIds are absolute, normalized paths
648
+ chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
649
+ );
650
+ for (const step of currentOutput.steps) {
651
+ const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
652
+ if (effectedChunk)
653
+ changes.push(step);
654
+ }
655
+ const effectedAsset = currentOutput.publicAssets.find(
656
+ (chunk) => isChunkEffected(chunk)
657
+ );
658
+ if (effectedAsset)
659
+ changes.push(effectedAsset);
660
+ return changes;
661
+ }
662
+
663
+ // src/core/utils/building/find-entrypoints.ts
664
+ import { relative as relative4, resolve as resolve12 } from "path";
665
+ import fs12 from "fs-extra";
666
+ import { minimatch } from "minimatch";
667
+ import { parseHTML as parseHTML2 } from "linkedom";
668
+ import JSON5 from "json5";
669
+
670
+ // src/core/utils/building/build-entrypoints.ts
671
+ import * as vite2 from "vite";
672
+
673
+ // src/core/utils/fs.ts
674
+ import fs3 from "fs-extra";
675
+ import glob from "fast-glob";
676
+ import path3 from "node:path";
677
+ async function writeFileIfDifferent(file, newContents) {
678
+ const existingContents = await fs3.readFile(file, "utf-8").catch(() => void 0);
679
+ if (existingContents !== newContents) {
680
+ await fs3.writeFile(file, newContents);
681
+ }
682
+ }
683
+ async function getPublicFiles(config) {
684
+ if (!await fs3.exists(config.publicDir))
685
+ return [];
686
+ const files = await glob("**/*", { cwd: config.publicDir });
687
+ return files.map(unnormalizePath);
688
+ }
689
+ async function removeEmptyDirs(dir) {
690
+ const files = await fs3.readdir(dir);
691
+ for (const file of files) {
692
+ const filePath = path3.join(dir, file);
693
+ const stats = await fs3.stat(filePath);
694
+ if (stats.isDirectory()) {
695
+ await removeEmptyDirs(filePath);
696
+ }
697
+ }
698
+ try {
699
+ await fs3.rmdir(dir);
700
+ } catch {
701
+ }
702
+ }
703
+
704
+ // src/core/utils/building/build-entrypoints.ts
705
+ import fs4 from "fs-extra";
706
+ import { dirname as dirname3, resolve as resolve5 } from "path";
707
+ import pc from "picocolors";
708
+ async function buildEntrypoints(groups, config, spinner) {
709
+ const steps = [];
710
+ for (let i = 0; i < groups.length; i++) {
711
+ const group = groups[i];
712
+ spinner.text = pc.dim(`[${i + 1}/${groups.length}]`) + ` ${[group].flat().map((e) => e.name).join(pc.dim(", "))}`;
713
+ const step = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config);
714
+ steps.push(step);
715
+ }
716
+ const publicAssets = await copyPublicDirectory(config);
717
+ await removeEmptyDirs(config.outDir);
718
+ return { publicAssets, steps };
719
+ }
720
+ async function buildSingleEntrypoint(entrypoint, config) {
721
+ const isVirtual = [
722
+ "background",
723
+ "content-script",
724
+ "unlisted-script"
725
+ ].includes(entrypoint.type);
726
+ const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
727
+ const plugins = [];
728
+ if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
729
+ plugins.push(cssEntrypoints(entrypoint, config));
730
+ }
731
+ const libMode = {
732
+ plugins,
733
+ build: {
734
+ lib: {
735
+ entry,
736
+ formats: ["iife"],
737
+ name: "_",
738
+ fileName: entrypoint.name
739
+ },
740
+ rollupOptions: {
741
+ output: {
742
+ // There's only a single output for this build, so we use the desired bundle path for the
743
+ // entry output (like "content-scripts/overlay.js")
744
+ entryFileNames: getEntrypointBundlePath(
745
+ entrypoint,
746
+ config.outDir,
747
+ ".js"
748
+ ),
749
+ // Output content script CSS to `content-scripts/`, but all other scripts are written to
750
+ // `assets/`.
751
+ assetFileNames: ({ name }) => {
752
+ if (entrypoint.type === "content-script" && name?.endsWith("css")) {
753
+ return `content-scripts/${entrypoint.name}.[ext]`;
754
+ } else {
755
+ return `assets/${entrypoint.name}.[ext]`;
756
+ }
757
+ }
758
+ }
759
+ }
760
+ },
761
+ define: {
762
+ // See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
763
+ "process.env.NODE_ENV": JSON.stringify(config.mode)
764
+ }
765
+ };
766
+ for (const global of getEntrypointGlobals(config, entrypoint.name)) {
767
+ libMode.define[global.name] = JSON.stringify(global.value);
768
+ }
769
+ const entryConfig = vite2.mergeConfig(
770
+ libMode,
771
+ await config.vite(config.env)
772
+ );
773
+ const result = await vite2.build(entryConfig);
774
+ return {
775
+ entrypoints: entrypoint,
776
+ chunks: getBuildOutputChunks(result)
777
+ };
778
+ }
779
+ async function buildMultipleEntrypoints(entrypoints, config) {
780
+ const multiPage = {
781
+ plugins: [multipageMove(entrypoints, config)],
782
+ build: {
783
+ rollupOptions: {
784
+ input: entrypoints.reduce((input, entry) => {
785
+ input[entry.name] = entry.inputPath;
786
+ return input;
787
+ }, {}),
788
+ output: {
789
+ // Include a hash to prevent conflicts
790
+ chunkFileNames: "chunks/[name]-[hash].js",
791
+ // Include a hash to prevent conflicts
792
+ entryFileNames: "chunks/[name]-[hash].js",
793
+ // We can't control the "name", so we need a hash to prevent conflicts
794
+ assetFileNames: "assets/[name]-[hash].[ext]"
795
+ }
796
+ }
797
+ },
798
+ define: {}
799
+ };
800
+ for (const global of getEntrypointGlobals(config, "html")) {
801
+ multiPage.define[global.name] = JSON.stringify(global.value);
802
+ }
803
+ const entryConfig = vite2.mergeConfig(
804
+ multiPage,
805
+ await config.vite(config.env)
806
+ );
807
+ const result = await vite2.build(entryConfig);
808
+ return {
809
+ entrypoints,
810
+ chunks: getBuildOutputChunks(result)
811
+ };
812
+ }
813
+ function getBuildOutputChunks(result) {
814
+ if ("on" in result)
815
+ throw Error("wxt does not support vite watch mode.");
816
+ if (Array.isArray(result))
817
+ return result.flatMap(({ output }) => output);
818
+ return result.output;
819
+ }
820
+ async function copyPublicDirectory(config) {
821
+ const files = await getPublicFiles(config);
822
+ if (files.length === 0)
823
+ return [];
824
+ const publicAssets = [];
825
+ for (const file of files) {
826
+ const srcPath = resolve5(config.publicDir, file);
827
+ const outPath = resolve5(config.outDir, file);
828
+ await fs4.ensureDir(dirname3(outPath));
829
+ await fs4.copyFile(srcPath, outPath);
830
+ publicAssets.push({
831
+ type: "asset",
832
+ fileName: file,
833
+ name: file,
834
+ needsCodeReference: false,
835
+ source: await fs4.readFile(srcPath)
836
+ });
837
+ }
838
+ return publicAssets;
839
+ }
840
+
841
+ // src/core/utils/building/generate-wxt-dir.ts
842
+ import { createUnimport as createUnimport2 } from "unimport";
843
+ import fs5 from "fs-extra";
844
+ import { relative as relative3, resolve as resolve6 } from "path";
845
+ import path4 from "node:path";
846
+
847
+ // src/core/utils/i18n.ts
848
+ var predefinedMessages = {
849
+ "@@extension_id": {
850
+ message: "<browser.runtime.id>",
851
+ 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."
852
+ },
853
+ "@@ui_locale": {
854
+ message: "<browser.i18n.getUiLocale()>",
855
+ description: ""
856
+ },
857
+ "@@bidi_dir": {
858
+ message: "<ltr|rtl>",
859
+ 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.'
860
+ },
861
+ "@@bidi_reversed_dir": {
862
+ message: "<rtl|ltr>",
863
+ description: `If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".`
864
+ },
865
+ "@@bidi_start_edge": {
866
+ message: "<left|right>",
867
+ description: `If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".`
868
+ },
869
+ "@@bidi_end_edge": {
870
+ message: "<right|left>",
871
+ description: `If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".`
872
+ }
873
+ };
874
+ function parseI18nMessages(messagesJson) {
875
+ return Object.entries({
876
+ ...predefinedMessages,
877
+ ...messagesJson
878
+ }).map(([name, details]) => ({
879
+ name,
880
+ ...details
881
+ }));
882
+ }
883
+
884
+ // src/core/utils/building/generate-wxt-dir.ts
885
+ async function generateTypesDir(entrypoints, config) {
886
+ await fs5.ensureDir(config.typesDir);
887
+ const references = [];
888
+ const imports = getUnimportOptions(config);
889
+ if (imports !== false) {
890
+ references.push(await writeImportsDeclarationFile(config, imports));
891
+ }
892
+ references.push(await writePathsDeclarationFile(entrypoints, config));
893
+ references.push(await writeI18nDeclarationFile(config));
894
+ references.push(await writeGlobalsDeclarationFile(config));
895
+ const mainReference = await writeMainDeclarationFile(references, config);
896
+ await writeTsConfigFile(mainReference, config);
897
+ }
898
+ async function writeImportsDeclarationFile(config, unimportOptions) {
899
+ const filePath = resolve6(config.typesDir, "imports.d.ts");
900
+ const unimport2 = createUnimport2(unimportOptions);
901
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
902
+ await writeFileIfDifferent(
903
+ filePath,
904
+ ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
905
+ "\n"
906
+ ) + "\n"
907
+ );
908
+ return filePath;
909
+ }
910
+ async function writePathsDeclarationFile(entrypoints, config) {
911
+ const filePath = resolve6(config.typesDir, "paths.d.ts");
912
+ const unions = entrypoints.map(
913
+ (entry) => getEntrypointBundlePath(
914
+ entry,
915
+ config.outDir,
916
+ entry.inputPath.endsWith(".html") ? ".html" : ".js"
917
+ )
918
+ ).concat(await getPublicFiles(config)).map(normalizePath2).map((path7) => ` | "/${path7}"`).sort().join("\n");
919
+ const template = `// Generated by wxt
920
+ import "wxt/browser";
921
+
922
+ declare module "wxt/browser" {
923
+ type PublicPath =
924
+ {{ union }}
925
+ export interface WxtRuntime extends Runtime.Static {
926
+ getURL(path: PublicPath): string;
927
+ }
928
+ }
929
+ `;
930
+ await writeFileIfDifferent(
931
+ filePath,
932
+ template.replace("{{ union }}", unions || " | never")
933
+ );
934
+ return filePath;
935
+ }
936
+ async function writeI18nDeclarationFile(config) {
937
+ const filePath = resolve6(config.typesDir, "i18n.d.ts");
938
+ const defaultLocale = config.manifest.default_locale;
939
+ const template = `// Generated by wxt
940
+ import "wxt/browser";
941
+
942
+ declare module "wxt/browser" {
943
+ /**
944
+ * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
945
+ */
946
+ interface GetMessageOptions {
947
+ /**
948
+ * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
949
+ */
950
+ escapeLt?: boolean
951
+ }
952
+
953
+ export interface WxtI18n extends I18n.Static {
954
+ {{ overrides }}
955
+ }
956
+ }
957
+ `;
958
+ let messages;
959
+ if (defaultLocale) {
960
+ const defaultLocalePath = path4.resolve(
961
+ config.publicDir,
962
+ "_locales",
963
+ defaultLocale,
964
+ "messages.json"
965
+ );
966
+ const content = JSON.parse(await fs5.readFile(defaultLocalePath, "utf-8"));
967
+ messages = parseI18nMessages(content);
968
+ } else {
969
+ messages = parseI18nMessages({});
970
+ }
971
+ const overrides = messages.map((message) => {
972
+ return ` /**
973
+ * ${message.description ?? "No message description."}
974
+ *
975
+ * "${message.message}"
976
+ */
977
+ getMessage(
978
+ messageName: "${message.name}",
979
+ substitutions?: string | string[],
980
+ options?: GetMessageOptions,
981
+ ): string;`;
982
+ });
983
+ await writeFileIfDifferent(
984
+ filePath,
985
+ template.replace("{{ overrides }}", overrides.join("\n"))
986
+ );
987
+ return filePath;
988
+ }
989
+ async function writeGlobalsDeclarationFile(config) {
990
+ const filePath = resolve6(config.typesDir, "globals.d.ts");
991
+ const globals2 = [...getGlobals(config), ...getEntrypointGlobals(config, "")];
992
+ await writeFileIfDifferent(
993
+ filePath,
994
+ [
995
+ "// Generated by wxt",
996
+ "export {}",
997
+ "declare global {",
998
+ ...globals2.map((global) => ` const ${global.name}: ${global.type};`),
999
+ "}"
1000
+ ].join("\n") + "\n"
1001
+ );
1002
+ return filePath;
1003
+ }
1004
+ async function writeMainDeclarationFile(references, config) {
1005
+ const dir = config.wxtDir;
1006
+ const filePath = resolve6(dir, "wxt.d.ts");
1007
+ await writeFileIfDifferent(
1008
+ filePath,
1009
+ [
1010
+ "// Generated by wxt",
1011
+ `/// <reference types="vite/client" />`,
1012
+ ...references.map(
1013
+ (ref) => `/// <reference types="./${normalizePath2(relative3(dir, ref))}" />`
1014
+ )
1015
+ ].join("\n") + "\n"
1016
+ );
1017
+ return filePath;
1018
+ }
1019
+ async function writeTsConfigFile(mainReference, config) {
1020
+ const dir = config.wxtDir;
1021
+ const rootPath = normalizePath2(relative3(dir, config.root));
1022
+ const srcPath = normalizePath2(relative3(dir, config.srcDir));
1023
+ await writeFileIfDifferent(
1024
+ resolve6(dir, "tsconfig.json"),
1025
+ `{
1026
+ "compilerOptions": {
1027
+ "target": "ESNext",
1028
+ "module": "ESNext",
1029
+ "moduleResolution": "Bundler",
1030
+ "noEmit": true,
1031
+ "esModuleInterop": true,
1032
+ "forceConsistentCasingInFileNames": true,
1033
+ "resolveJsonModule": true,
1034
+ "strict": true,
1035
+ "lib": ["DOM", "WebWorker"],
1036
+ "skipLibCheck": true,
1037
+ "paths": {
1038
+ "@": ["${srcPath}"],
1039
+ "@/*": ["${srcPath}/*"],
1040
+ "~": ["${srcPath}"],
1041
+ "~/*": ["${srcPath}/*"],
1042
+ "@@": ["${rootPath}"],
1043
+ "@@/*": ["${rootPath}/*"],
1044
+ "~~": ["${rootPath}"],
1045
+ "~~/*": ["${rootPath}/*"]
1046
+ }
1047
+ },
1048
+ "include": [
1049
+ "${normalizePath2(relative3(dir, config.root))}/**/*",
1050
+ "./${normalizePath2(relative3(dir, mainReference))}"
1051
+ ],
1052
+ "exclude": ["${normalizePath2(relative3(dir, config.outBaseDir))}"]
1053
+ }`
1054
+ );
1055
+ }
1056
+
1057
+ // src/core/utils/building/get-internal-config.ts
1058
+ import { loadConfig } from "c12";
1059
+ import path5 from "node:path";
1060
+ import * as vite3 from "vite";
1061
+
1062
+ // src/core/utils/cache.ts
1063
+ import fs6, { ensureDir as ensureDir2 } from "fs-extra";
1064
+ import { dirname as dirname4, resolve as resolve7 } from "path";
1065
+ function createFsCache(wxtDir) {
1066
+ const getPath = (key) => resolve7(wxtDir, "cache", encodeURIComponent(key));
1067
+ return {
1068
+ async set(key, value) {
1069
+ const path7 = getPath(key);
1070
+ await ensureDir2(dirname4(path7));
1071
+ await writeFileIfDifferent(path7, value);
1072
+ },
1073
+ async get(key) {
1074
+ const path7 = getPath(key);
1075
+ try {
1076
+ return await fs6.readFile(path7, "utf-8");
1077
+ } catch {
1078
+ return void 0;
1079
+ }
1080
+ }
1081
+ };
1082
+ }
1083
+
1084
+ // src/core/utils/building/get-internal-config.ts
1085
+ import consola, { LogLevels } from "consola";
1086
+ async function getInternalConfig(inlineConfig, command) {
1087
+ let userConfig = {};
1088
+ let userConfigMetadata;
1089
+ if (inlineConfig.configFile !== false) {
1090
+ const { config: loadedConfig, ...metadata } = await loadConfig({
1091
+ name: "wxt",
1092
+ cwd: inlineConfig.root ?? process.cwd(),
1093
+ rcFile: false
1094
+ });
1095
+ userConfig = loadedConfig ?? {};
1096
+ userConfigMetadata = metadata;
1097
+ }
1098
+ const mergedConfig = mergeInlineConfig(inlineConfig, userConfig);
1099
+ const debug = mergedConfig.debug ?? false;
1100
+ const logger = mergedConfig.logger ?? consola;
1101
+ if (debug)
1102
+ logger.level = LogLevels.debug;
1103
+ const browser = mergedConfig.browser ?? "chrome";
1104
+ const manifestVersion = mergedConfig.manifestVersion ?? (browser === "firefox" || browser === "safari" ? 2 : 3);
1105
+ const mode = mergedConfig.mode ?? (command === "build" ? "production" : "development");
1106
+ const env = { browser, command, manifestVersion, mode };
1107
+ const root = path5.resolve(
1108
+ inlineConfig.root ?? userConfig.root ?? process.cwd()
1109
+ );
1110
+ const wxtDir = path5.resolve(root, ".wxt");
1111
+ const srcDir = path5.resolve(root, mergedConfig.srcDir ?? root);
1112
+ const entrypointsDir = path5.resolve(
1113
+ srcDir,
1114
+ mergedConfig.entrypointsDir ?? "entrypoints"
1115
+ );
1116
+ const publicDir = path5.resolve(srcDir, mergedConfig.publicDir ?? "public");
1117
+ const typesDir = path5.resolve(wxtDir, "types");
1118
+ const outBaseDir = path5.resolve(root, ".output");
1119
+ const outDir = path5.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
1120
+ const runnerConfig = await loadConfig({
1121
+ name: "web-ext",
1122
+ cwd: root,
1123
+ globalRc: true,
1124
+ rcFile: ".webextrc",
1125
+ overrides: inlineConfig.runner,
1126
+ defaults: userConfig.runner
1127
+ });
1128
+ const finalConfig = {
1129
+ browser,
1130
+ command,
1131
+ debug,
1132
+ entrypointsDir,
1133
+ env,
1134
+ fsCache: createFsCache(wxtDir),
1135
+ imports: mergedConfig.imports ?? {},
1136
+ logger,
1137
+ manifest: await resolveManifestConfig(env, mergedConfig.manifest),
1138
+ manifestVersion,
1139
+ mode,
1140
+ outBaseDir,
1141
+ outDir,
1142
+ publicDir,
1143
+ root,
1144
+ runnerConfig,
1145
+ srcDir,
1146
+ typesDir,
1147
+ vite: () => ({}),
1148
+ // Real value added after this object is initialized.
1149
+ wxtDir,
1150
+ zip: resolveInternalZipConfig(root, mergedConfig),
1151
+ transformManifest(manifest) {
1152
+ userConfig.transformManifest?.(manifest);
1153
+ inlineConfig.transformManifest?.(manifest);
1154
+ },
1155
+ analysis: {
1156
+ enabled: mergedConfig.analysis?.enabled ?? false,
1157
+ template: mergedConfig.analysis?.template ?? "treemap"
1158
+ },
1159
+ userConfigMetadata: userConfigMetadata ?? {}
1160
+ };
1161
+ finalConfig.vite = (env2) => resolveInternalViteConfig(env2, mergedConfig, finalConfig);
1162
+ return finalConfig;
1163
+ }
1164
+ async function resolveManifestConfig(env, manifest) {
1165
+ return await (typeof manifest === "function" ? manifest(env) : manifest ?? {});
1166
+ }
1167
+ function mergeInlineConfig(inlineConfig, userConfig) {
1168
+ let imports;
1169
+ if (inlineConfig.imports === false || userConfig.imports === false) {
1170
+ imports = false;
1171
+ } else if (userConfig.imports == null && inlineConfig.imports == null) {
1172
+ imports = void 0;
1173
+ } else {
1174
+ imports = vite3.mergeConfig(
1175
+ userConfig.imports ?? {},
1176
+ inlineConfig.imports ?? {}
1177
+ );
1178
+ }
1179
+ const manifest = async (env) => {
1180
+ const user = await resolveManifestConfig(env, userConfig.manifest);
1181
+ const inline = await resolveManifestConfig(env, inlineConfig.manifest);
1182
+ return vite3.mergeConfig(user, inline);
1183
+ };
1184
+ const viteConfig = async (env) => {
1185
+ const user = await userConfig.vite?.(env);
1186
+ const inline = await inlineConfig.vite?.(env);
1187
+ return vite3.mergeConfig(user ?? {}, inline ?? {});
1188
+ };
1189
+ const runner = vite3.mergeConfig(
1190
+ userConfig.runner ?? {},
1191
+ inlineConfig.runner ?? {}
1192
+ );
1193
+ const zip = vite3.mergeConfig(
1194
+ userConfig.zip ?? {},
1195
+ inlineConfig.zip ?? {}
1196
+ );
1197
+ return {
1198
+ root: inlineConfig.root ?? userConfig.root,
1199
+ browser: inlineConfig.browser ?? userConfig.browser,
1200
+ manifestVersion: inlineConfig.manifestVersion ?? userConfig.manifestVersion,
1201
+ configFile: inlineConfig.configFile,
1202
+ debug: inlineConfig.debug ?? userConfig.debug,
1203
+ entrypointsDir: inlineConfig.entrypointsDir ?? userConfig.entrypointsDir,
1204
+ imports,
1205
+ logger: inlineConfig.logger ?? userConfig.logger,
1206
+ manifest,
1207
+ mode: inlineConfig.mode ?? userConfig.mode,
1208
+ publicDir: inlineConfig.publicDir ?? userConfig.publicDir,
1209
+ runner,
1210
+ srcDir: inlineConfig.srcDir ?? userConfig.srcDir,
1211
+ vite: viteConfig,
1212
+ zip,
1213
+ analysis: {
1214
+ enabled: inlineConfig.analysis?.enabled ?? userConfig.analysis?.enabled,
1215
+ template: inlineConfig.analysis?.template ?? userConfig.analysis?.template
1216
+ }
1217
+ };
1218
+ }
1219
+ function resolveInternalZipConfig(root, mergedConfig) {
1220
+ return {
1221
+ sourcesTemplate: "{{name}}-{{version}}-sources.zip",
1222
+ artifactTemplate: "{{name}}-{{version}}-{{browser}}.zip",
1223
+ sourcesRoot: root,
1224
+ ...mergedConfig.zip,
1225
+ ignoredSources: [
1226
+ "**/node_modules",
1227
+ // WXT files
1228
+ "**/web-ext.config.ts",
1229
+ // Hidden files
1230
+ "**/.*",
1231
+ // Tests
1232
+ "**/__tests__/**",
1233
+ "**/*.+(test|spec).?(c|m)+(j|t)s?(x)",
1234
+ // From user
1235
+ ...mergedConfig.zip?.ignoredSources ?? []
1236
+ ]
1237
+ };
1238
+ }
1239
+ async function resolveInternalViteConfig(env, mergedConfig, finalConfig) {
1240
+ const internalVite = await mergedConfig.vite?.(env) ?? {};
1241
+ internalVite.root = finalConfig.root;
1242
+ internalVite.configFile = false;
1243
+ internalVite.logLevel = "warn";
1244
+ internalVite.mode = env.mode;
1245
+ internalVite.build ??= {};
1246
+ internalVite.build.outDir = finalConfig.outDir;
1247
+ internalVite.build.emptyOutDir = false;
1248
+ internalVite.plugins ??= [];
1249
+ internalVite.plugins.push(download(finalConfig));
1250
+ internalVite.plugins.push(devHtmlPrerender(finalConfig));
1251
+ internalVite.plugins.push(unimport(finalConfig));
1252
+ internalVite.plugins.push(
1253
+ virtualEntrypoint("background", finalConfig)
1254
+ );
1255
+ internalVite.plugins.push(
1256
+ virtualEntrypoint("content-script", finalConfig)
1257
+ );
1258
+ internalVite.plugins.push(
1259
+ virtualEntrypoint("unlisted-script", finalConfig)
1260
+ );
1261
+ internalVite.plugins.push(devServerGlobals(finalConfig));
1262
+ internalVite.plugins.push(tsconfigPaths(finalConfig));
1263
+ internalVite.plugins.push(noopBackground());
1264
+ if (finalConfig.analysis.enabled) {
1265
+ internalVite.plugins.push(bundleAnalysis());
1266
+ }
1267
+ internalVite.plugins.push(globals(finalConfig));
1268
+ return internalVite;
1269
+ }
1270
+
1271
+ // src/core/utils/building/group-entrypoints.ts
1272
+ function groupEntrypoints(entrypoints) {
1273
+ const groupIndexMap = {};
1274
+ const groups = [];
1275
+ for (const entry of entrypoints) {
1276
+ const group = ENTRY_TYPE_TO_GROUP_MAP[entry.type];
1277
+ if (group === "no-group") {
1278
+ groups.push(entry);
1279
+ } else {
1280
+ let groupIndex = groupIndexMap[group];
1281
+ if (groupIndex == null) {
1282
+ groupIndex = groups.push([]) - 1;
1283
+ groupIndexMap[group] = groupIndex;
1284
+ }
1285
+ groups[groupIndex].push(entry);
1286
+ }
1287
+ }
1288
+ return groups;
1289
+ }
1290
+ var ENTRY_TYPE_TO_GROUP_MAP = {
1291
+ sandbox: "sandbox-page",
1292
+ popup: "extension-page",
1293
+ newtab: "extension-page",
1294
+ history: "extension-page",
1295
+ options: "extension-page",
1296
+ devtools: "extension-page",
1297
+ bookmarks: "extension-page",
1298
+ sidepanel: "extension-page",
1299
+ "unlisted-page": "extension-page",
1300
+ background: "no-group",
1301
+ "content-script": "no-group",
1302
+ "unlisted-script": "no-group",
1303
+ "unlisted-style": "no-group",
1304
+ "content-script-style": "no-group"
1305
+ };
1306
+
1307
+ // src/core/utils/building/import-entrypoint.ts
1308
+ import createJITI from "jiti";
1309
+ import { createUnimport as createUnimport3 } from "unimport";
1310
+ import fs7 from "fs-extra";
1311
+ import { resolve as resolve8 } from "path";
1312
+
1313
+ // src/core/utils/strings.ts
1314
+ function kebabCaseAlphanumeric(str) {
1315
+ return str.toLowerCase().replace(/[^a-z0-9-\s]/g, "").replace(/\s+/g, "-");
1316
+ }
1317
+ function removeImportStatements(text) {
1318
+ return text.replace(
1319
+ /(import\s?[{\w][\s\S]*?from\s?["'][\s\S]*?["'];?|import\s?["'][\s\S]*?["'];?)/gm,
1320
+ ""
1321
+ );
1322
+ }
1323
+ function removeProjectImportStatements(text) {
1324
+ const noImports = removeImportStatements(text);
1325
+ return `import { defineContentScript, defineBackground } from 'wxt/client';
1326
+ import { defineUnlistedScript } from 'wxt/sandbox';
1327
+
1328
+ ${noImports}`;
1329
+ }
1330
+
1331
+ // src/core/utils/building/import-entrypoint.ts
1332
+ import { transformSync } from "esbuild";
1333
+ async function importEntrypointFile(path7, config) {
1334
+ config.logger.debug("Loading file metadata:", path7);
1335
+ const normalPath = normalizePath2(path7);
1336
+ const unimport2 = createUnimport3({
1337
+ ...getUnimportOptions(config),
1338
+ // Only allow specific imports, not all from the project
1339
+ dirs: []
1340
+ });
1341
+ await unimport2.init();
1342
+ const text = await fs7.readFile(path7, "utf-8");
1343
+ const textNoImports = removeProjectImportStatements(text);
1344
+ const { code } = await unimport2.injectImports(textNoImports);
1345
+ config.logger.debug(
1346
+ ["Text:", text, "No imports:", textNoImports, "Code:", code].join("\n")
1347
+ );
1348
+ const jiti = createJITI(__filename, {
1349
+ cache: false,
1350
+ debug: config.debug,
1351
+ esmResolve: true,
1352
+ alias: {
1353
+ "webextension-polyfill": resolve8(
1354
+ config.root,
1355
+ "node_modules/wxt/dist/virtual/mock-browser.js"
1356
+ )
1357
+ },
1358
+ // List of extensions to transform with esbuild
1359
+ extensions: [".ts", ".cts", ".mts", ".tsx", ".js", ".cjs", ".mjs", ".jsx"],
1360
+ transform(opts) {
1361
+ const isEntrypoint = opts.filename === normalPath;
1362
+ return transformSync(
1363
+ // Use modified source code for entrypoints
1364
+ isEntrypoint ? code : opts.source,
1365
+ getEsbuildOptions(opts)
1366
+ );
1367
+ }
1368
+ });
1369
+ try {
1370
+ const res = await jiti(path7);
1371
+ return res.default;
1372
+ } catch (err) {
1373
+ config.logger.error(err);
1374
+ throw err;
1375
+ }
1376
+ }
1377
+ function getEsbuildOptions(opts) {
1378
+ const isJsx = opts.filename?.endsWith("x");
1379
+ return {
1380
+ format: "cjs",
1381
+ loader: isJsx ? "tsx" : "ts",
1382
+ jsx: isJsx ? "automatic" : void 0
1383
+ };
1384
+ }
1385
+
1386
+ // src/core/utils/building/internal-build.ts
1387
+ import pc4 from "picocolors";
1388
+ import * as vite5 from "vite";
1389
+ import fs11 from "fs-extra";
1390
+
1391
+ // src/core/utils/log/printBuildSummary.ts
1392
+ import { resolve as resolve9 } from "path";
1393
+
1394
+ // src/core/utils/log/printFileList.ts
1395
+ import path6 from "node:path";
1396
+ import pc2 from "picocolors";
1397
+ import fs8 from "fs-extra";
1398
+ import { filesize } from "filesize";
1399
+
1400
+ // src/core/utils/log/printTable.ts
1401
+ function printTable(log, header, rows, gap = 2) {
1402
+ if (rows.length === 0)
1403
+ return;
1404
+ const columnWidths = rows.reduce(
1405
+ (widths, row) => {
1406
+ for (let i = 0; i < Math.max(widths.length, row.length); i++) {
1407
+ widths[i] = Math.max(row[i]?.length ?? 0, widths[i] ?? 0);
1408
+ }
1409
+ return widths;
1410
+ },
1411
+ rows[0].map((column) => column.length)
1412
+ );
1413
+ let str = "";
1414
+ rows.forEach((row, i) => {
1415
+ row.forEach((col, j) => {
1416
+ str += col.padEnd(columnWidths[j], " ");
1417
+ if (j !== row.length - 1)
1418
+ str += "".padEnd(gap, " ");
1419
+ });
1420
+ if (i !== rows.length - 1)
1421
+ str += "\n";
1422
+ });
1423
+ log(`${header}
1424
+ ${str}`);
1425
+ }
1426
+
1427
+ // src/core/utils/log/printFileList.ts
1428
+ async function printFileList(log, header, baseDir, files) {
1429
+ let totalSize = 0;
1430
+ const fileRows = await Promise.all(
1431
+ files.map(async (file, i) => {
1432
+ const parts = [
1433
+ path6.relative(process.cwd(), baseDir) + path6.sep,
1434
+ path6.relative(baseDir, file)
1435
+ ];
1436
+ const prefix = i === files.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
1437
+ const color = getChunkColor(file);
1438
+ const stats = await fs8.lstat(file);
1439
+ totalSize += stats.size;
1440
+ const size = String(filesize(stats.size));
1441
+ return [
1442
+ `${pc2.gray(prefix)} ${pc2.dim(parts[0])}${color(parts[1])}`,
1443
+ pc2.dim(size)
1444
+ ];
1445
+ })
1446
+ );
1447
+ fileRows.push([`${pc2.cyan("\u03A3 Total size:")} ${String(filesize(totalSize))}`]);
1448
+ printTable(log, header, fileRows);
1449
+ }
1450
+ var DEFAULT_COLOR = pc2.blue;
1451
+ var CHUNK_COLORS = {
1452
+ ".js.map": pc2.gray,
1453
+ ".cjs.map": pc2.gray,
1454
+ ".mjs.map": pc2.gray,
1455
+ ".html": pc2.green,
1456
+ ".css": pc2.magenta,
1457
+ ".js": pc2.cyan,
1458
+ ".cjs": pc2.cyan,
1459
+ ".mjs": pc2.cyan,
1460
+ ".zip": pc2.yellow
1461
+ };
1462
+ function getChunkColor(filename) {
1463
+ return Object.entries(CHUNK_COLORS).find(([key]) => filename.endsWith(key))?.[1] ?? DEFAULT_COLOR;
1464
+ }
1465
+
1466
+ // src/core/utils/log/printBuildSummary.ts
1467
+ async function printBuildSummary(log, header, output, config) {
1468
+ const chunks = [
1469
+ ...output.steps.flatMap((step) => step.chunks),
1470
+ ...output.publicAssets
1471
+ ].sort((l, r) => {
1472
+ const lWeight = getChunkSortWeight(l.fileName);
1473
+ const rWeight = getChunkSortWeight(r.fileName);
1474
+ const diff = lWeight - rWeight;
1475
+ if (diff !== 0)
1476
+ return diff;
1477
+ return l.fileName.localeCompare(r.fileName);
1478
+ });
1479
+ const files = chunks.map((chunk) => resolve9(config.outDir, chunk.fileName));
1480
+ await printFileList(log, header, config.outDir, files);
1481
+ }
1482
+ var DEFAULT_SORT_WEIGHT = 100;
1483
+ var CHUNK_SORT_WEIGHTS = {
1484
+ "manifest.json": 0,
1485
+ ".html": 1,
1486
+ ".js.map": 2,
1487
+ ".js": 2,
1488
+ ".css": 3
1489
+ };
1490
+ function getChunkSortWeight(filename) {
1491
+ return Object.entries(CHUNK_SORT_WEIGHTS).find(
1492
+ ([key]) => filename.endsWith(key)
1493
+ )?.[1] ?? DEFAULT_SORT_WEIGHT;
1494
+ }
1495
+
1496
+ // src/core/utils/log/printHeader.ts
1497
+ import pc3 from "picocolors";
1498
+ import { consola as consola2 } from "consola";
1499
+
1500
+ // src/core/utils/building/internal-build.ts
1501
+ import glob2 from "fast-glob";
1502
+
1503
+ // src/core/utils/manifest.ts
1504
+ import fs10 from "fs-extra";
1505
+ import { resolve as resolve11 } from "path";
1506
+
1507
+ // src/core/utils/content-security-policy.ts
1508
+ var ContentSecurityPolicy = class _ContentSecurityPolicy {
1509
+ static DIRECTIVE_ORDER = {
1510
+ "default-src": 0,
1511
+ "script-src": 1,
1512
+ "object-src": 2
1513
+ };
1514
+ data;
1515
+ constructor(csp) {
1516
+ if (csp) {
1517
+ const sections = csp.split(";").map((section) => section.trim());
1518
+ this.data = sections.reduce((data, section) => {
1519
+ const [key, ...values] = section.split(" ").map((item) => item.trim());
1520
+ if (key)
1521
+ data[key] = values;
1522
+ return data;
1523
+ }, {});
1524
+ } else {
1525
+ this.data = {};
1526
+ }
1527
+ }
1528
+ /**
1529
+ * Ensure a set of values are listed under a directive.
1530
+ */
1531
+ add(directive, ...newValues) {
1532
+ const values = this.data[directive] ?? [];
1533
+ newValues.forEach((newValue) => {
1534
+ if (!values.includes(newValue))
1535
+ values.push(newValue);
1536
+ });
1537
+ this.data[directive] = values;
1538
+ return this;
1539
+ }
1540
+ toString() {
1541
+ const directives = Object.entries(this.data).sort(([l], [r]) => {
1542
+ const lo = _ContentSecurityPolicy.DIRECTIVE_ORDER[l] ?? 2;
1543
+ const ro = _ContentSecurityPolicy.DIRECTIVE_ORDER[r] ?? 2;
1544
+ return lo - ro;
1545
+ });
1546
+ return directives.map((entry) => entry.flat().join(" ")).join("; ") + ";";
1547
+ }
1548
+ };
1549
+
1550
+ // src/core/utils/content-scripts.ts
1551
+ function hashContentScriptOptions(options, config) {
1552
+ const simplifiedOptions = mapWxtOptionsToContentScript(options, config);
1553
+ Object.keys(simplifiedOptions).forEach((key) => {
1554
+ if (simplifiedOptions[key] == null)
1555
+ delete simplifiedOptions[key];
1556
+ });
1557
+ const withDefaults = {
1558
+ exclude_globs: [],
1559
+ exclude_matches: [],
1560
+ include_globs: [],
1561
+ match_about_blank: false,
1562
+ run_at: "document_idle",
1563
+ all_frames: false,
1564
+ // @ts-expect-error - not in type
1565
+ match_origin_as_fallback: false,
1566
+ world: "ISOLATED",
1567
+ ...simplifiedOptions
1568
+ };
1569
+ return JSON.stringify(
1570
+ Object.entries(withDefaults).map(([key, value]) => {
1571
+ if (Array.isArray(value))
1572
+ return [key, value.sort()];
1573
+ else
1574
+ return [key, value];
1575
+ }).sort((l, r) => l[0].localeCompare(r[0]))
1576
+ );
1577
+ }
1578
+ function mapWxtOptionsToContentScript(options, config) {
1579
+ return {
1580
+ matches: resolvePerBrowserOption(options.matches, config.browser),
1581
+ all_frames: resolvePerBrowserOption(options.allFrames, config.browser),
1582
+ match_about_blank: resolvePerBrowserOption(
1583
+ options.matchAboutBlank,
1584
+ config.browser
1585
+ ),
1586
+ exclude_globs: resolvePerBrowserOption(
1587
+ options.excludeGlobs,
1588
+ config.browser
1589
+ ),
1590
+ exclude_matches: resolvePerBrowserOption(
1591
+ options.excludeMatches,
1592
+ config.browser
1593
+ ),
1594
+ include_globs: resolvePerBrowserOption(
1595
+ options.includeGlobs,
1596
+ config.browser
1597
+ ),
1598
+ run_at: resolvePerBrowserOption(options.runAt, config.browser),
1599
+ // @ts-expect-error: untyped chrome options
1600
+ match_origin_as_fallback: resolvePerBrowserOption(
1601
+ options.matchOriginAsFallback,
1602
+ config.browser
1603
+ ),
1604
+ world: options.world
1605
+ };
1606
+ }
1607
+
1608
+ // src/core/utils/package.ts
1609
+ import { resolve as resolve10 } from "node:path";
1610
+ import fs9 from "fs-extra";
1611
+ async function getPackageJson(config) {
1612
+ const file = resolve10(config.root, "package.json");
1613
+ try {
1614
+ return await fs9.readJson(file);
1615
+ } catch (err) {
1616
+ config.logger.debug(
1617
+ `Failed to read package.json at: ${file}. Returning undefined.`
1618
+ );
1619
+ return {};
1620
+ }
1621
+ }
1622
+
1623
+ // src/core/utils/manifest.ts
1624
+ import { produce } from "immer";
1625
+ import * as vite4 from "vite";
1626
+ async function writeManifest(manifest, output, config) {
1627
+ const str = config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
1628
+ await fs10.ensureDir(config.outDir);
1629
+ await writeFileIfDifferent(resolve11(config.outDir, "manifest.json"), str);
1630
+ output.publicAssets.unshift({
1631
+ type: "asset",
1632
+ fileName: "manifest.json",
1633
+ name: "manifest",
1634
+ needsCodeReference: false,
1635
+ source: str
1636
+ });
1637
+ }
1638
+ async function generateMainfest(entrypoints, buildOutput, config) {
1639
+ const pkg = await getPackageJson(config);
1640
+ const versionName = config.manifest.version_name ?? pkg?.version;
1641
+ const version3 = config.manifest.version ?? simplifyVersion(pkg?.version);
1642
+ const baseManifest = {
1643
+ manifest_version: config.manifestVersion,
1644
+ name: pkg?.name,
1645
+ description: pkg?.description,
1646
+ version: version3,
1647
+ version_name: (
1648
+ // Firefox doesn't support version_name
1649
+ config.browser === "firefox" || versionName === version3 ? void 0 : versionName
1650
+ ),
1651
+ short_name: pkg?.shortName,
1652
+ icons: discoverIcons(buildOutput)
1653
+ };
1654
+ const userManifest = config.manifest;
1655
+ const manifest = vite4.mergeConfig(
1656
+ baseManifest,
1657
+ userManifest
1658
+ );
1659
+ addEntrypoints(manifest, entrypoints, buildOutput, config);
1660
+ if (config.command === "serve")
1661
+ addDevModeCsp(manifest, config);
1662
+ if (config.command === "serve")
1663
+ addDevModePermissions(manifest, config);
1664
+ const finalManifest = produce(manifest, config.transformManifest);
1665
+ if (finalManifest.name == null)
1666
+ throw Error(
1667
+ "Manifest 'name' is missing. Either:\n1. Set the name in your <rootDir>/package.json\n2. Set a name via the manifest option in your wxt.config.ts"
1668
+ );
1669
+ if (finalManifest.version == null) {
1670
+ throw Error(
1671
+ "Manifest 'version' is missing. Either:\n1. Add a version in your <rootDir>/package.json\n2. Pass the version via the manifest option in your wxt.config.ts"
1672
+ );
1673
+ }
1674
+ return finalManifest;
1675
+ }
1676
+ function simplifyVersion(versionName) {
1677
+ const version3 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
1678
+ versionName
1679
+ )?.[1];
1680
+ if (version3 == null)
1681
+ throw Error(
1682
+ `Cannot simplify package.json version "${versionName}" to a valid extension version, "X.Y.Z"`
1683
+ );
1684
+ return version3;
1685
+ }
1686
+ function addEntrypoints(manifest, entrypoints, buildOutput, config) {
1687
+ const entriesByType = entrypoints.reduce((map, entrypoint) => {
1688
+ map[entrypoint.type] ??= [];
1689
+ map[entrypoint.type]?.push(entrypoint);
1690
+ return map;
1691
+ }, {});
1692
+ const background = entriesByType["background"]?.[0];
1693
+ const bookmarks = entriesByType["bookmarks"]?.[0];
1694
+ const contentScripts = entriesByType["content-script"];
1695
+ const devtools = entriesByType["devtools"]?.[0];
1696
+ const history = entriesByType["history"]?.[0];
1697
+ const newtab = entriesByType["newtab"]?.[0];
1698
+ const options = entriesByType["options"]?.[0];
1699
+ const popup = entriesByType["popup"]?.[0];
1700
+ const sandboxes = entriesByType["sandbox"];
1701
+ const sidepanels = entriesByType["sidepanel"];
1702
+ if (background) {
1703
+ const script = getEntrypointBundlePath(background, config.outDir, ".js");
1704
+ if (manifest.manifest_version === 3) {
1705
+ manifest.background = {
1706
+ type: background.options.type,
1707
+ service_worker: script
1708
+ };
1709
+ } else {
1710
+ manifest.background = {
1711
+ persistent: background.options.persistent,
1712
+ scripts: [script]
1713
+ };
1714
+ }
1715
+ }
1716
+ if (bookmarks) {
1717
+ if (config.browser === "firefox") {
1718
+ config.logger.warn(
1719
+ "Bookmarks are not supported by Firefox. chrome_url_overrides.bookmarks was not added to the manifest"
1720
+ );
1721
+ } else {
1722
+ manifest.chrome_url_overrides ??= {};
1723
+ manifest.chrome_url_overrides.bookmarks = getEntrypointBundlePath(
1724
+ bookmarks,
1725
+ config.outDir,
1726
+ ".html"
1727
+ );
1728
+ }
1729
+ }
1730
+ if (history) {
1731
+ if (config.browser === "firefox") {
1732
+ config.logger.warn(
1733
+ "Bookmarks are not supported by Firefox. chrome_url_overrides.history was not added to the manifest"
1734
+ );
1735
+ } else {
1736
+ manifest.chrome_url_overrides ??= {};
1737
+ manifest.chrome_url_overrides.history = getEntrypointBundlePath(
1738
+ history,
1739
+ config.outDir,
1740
+ ".html"
1741
+ );
1742
+ }
1743
+ }
1744
+ if (newtab) {
1745
+ manifest.chrome_url_overrides ??= {};
1746
+ manifest.chrome_url_overrides.newtab = getEntrypointBundlePath(
1747
+ newtab,
1748
+ config.outDir,
1749
+ ".html"
1750
+ );
1751
+ }
1752
+ if (popup) {
1753
+ const default_popup = getEntrypointBundlePath(
1754
+ popup,
1755
+ config.outDir,
1756
+ ".html"
1757
+ );
1758
+ const options2 = {};
1759
+ if (popup.options.defaultIcon)
1760
+ options2.default_icon = popup.options.defaultIcon;
1761
+ if (popup.options.defaultTitle)
1762
+ options2.default_title = popup.options.defaultTitle;
1763
+ if (popup.options.browserStyle)
1764
+ options2.browser_style = popup.options.browserStyle;
1765
+ if (manifest.manifest_version === 3) {
1766
+ manifest.action = {
1767
+ ...manifest.action ?? {},
1768
+ ...options2,
1769
+ default_popup
1770
+ };
1771
+ } else {
1772
+ const key = popup.options.mv2Key ?? "browser_action";
1773
+ manifest[key] = {
1774
+ ...manifest[key] ?? {},
1775
+ ...options2,
1776
+ default_popup
1777
+ };
1778
+ }
1779
+ }
1780
+ if (devtools) {
1781
+ manifest.devtools_page = getEntrypointBundlePath(
1782
+ devtools,
1783
+ config.outDir,
1784
+ ".html"
1785
+ );
1786
+ }
1787
+ if (options) {
1788
+ const page = getEntrypointBundlePath(options, config.outDir, ".html");
1789
+ manifest.options_ui = {
1790
+ open_in_tab: options.options.openInTab,
1791
+ browser_style: config.browser === "firefox" ? options.options.browserStyle : void 0,
1792
+ chrome_style: config.browser !== "firefox" ? options.options.chromeStyle : void 0,
1793
+ page
1794
+ };
1795
+ }
1796
+ if (sandboxes?.length) {
1797
+ if (config.browser === "firefox") {
1798
+ config.logger.warn(
1799
+ "Sandboxed pages not supported by Firefox. sandbox.pages was not added to the manifest"
1800
+ );
1801
+ } else {
1802
+ manifest.sandbox = {
1803
+ pages: sandboxes.map(
1804
+ (entry) => getEntrypointBundlePath(entry, config.outDir, ".html")
1805
+ )
1806
+ };
1807
+ }
1808
+ }
1809
+ if (sidepanels?.length) {
1810
+ const defaultSidepanel = sidepanels.find((entry) => entry.name === "sidepanel") ?? sidepanels[0];
1811
+ const page = getEntrypointBundlePath(
1812
+ defaultSidepanel,
1813
+ config.outDir,
1814
+ ".html"
1815
+ );
1816
+ if (config.browser === "firefox") {
1817
+ manifest.sidebar_action = {
1818
+ // TODO: Add options to side panel
1819
+ // ...defaultSidepanel.options,
1820
+ default_panel: page
1821
+ };
1822
+ } else if (config.manifestVersion === 3) {
1823
+ manifest.side_panel = {
1824
+ default_path: page
1825
+ };
1826
+ } else {
1827
+ config.logger.warn(
1828
+ "Side panel not supported by Chromium using MV2. side_panel.default_path was not added to the manifest"
1829
+ );
1830
+ }
1831
+ }
1832
+ if (contentScripts?.length) {
1833
+ const cssMap = getContentScriptsCssMap(buildOutput, contentScripts);
1834
+ if (config.command === "serve" && config.manifestVersion === 3) {
1835
+ const hostPermissions = new Set(manifest.host_permissions ?? []);
1836
+ contentScripts.forEach((script) => {
1837
+ const matches = resolvePerBrowserOption(
1838
+ script.options.matches,
1839
+ config.browser
1840
+ );
1841
+ matches.forEach((matchPattern) => {
1842
+ hostPermissions.add(matchPattern);
1843
+ });
1844
+ });
1845
+ hostPermissions.forEach(
1846
+ (permission) => addHostPermission(manifest, permission)
1847
+ );
1848
+ } else {
1849
+ const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
1850
+ const hash = hashContentScriptOptions(script.options, config);
1851
+ if (map.has(hash))
1852
+ map.get(hash)?.push(script);
1853
+ else
1854
+ map.set(hash, [script]);
1855
+ return map;
1856
+ }, /* @__PURE__ */ new Map());
1857
+ const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
1858
+ ([, scripts]) => ({
1859
+ ...mapWxtOptionsToContentScript(scripts[0].options, config),
1860
+ css: getContentScriptCssFiles(scripts, cssMap),
1861
+ js: scripts.map(
1862
+ (entry) => getEntrypointBundlePath(entry, config.outDir, ".js")
1863
+ )
1864
+ })
1865
+ );
1866
+ if (newContentScripts.length >= 0) {
1867
+ manifest.content_scripts ??= [];
1868
+ manifest.content_scripts.push(...newContentScripts);
1869
+ }
1870
+ }
1871
+ const contentScriptCssResources = getContentScriptCssWebAccessibleResources(
1872
+ config,
1873
+ contentScripts,
1874
+ cssMap
1875
+ );
1876
+ if (contentScriptCssResources.length > 0) {
1877
+ manifest.web_accessible_resources ??= [];
1878
+ manifest.web_accessible_resources.push(...contentScriptCssResources);
1879
+ }
1880
+ }
1881
+ }
1882
+ function discoverIcons(buildOutput) {
1883
+ const icons = [];
1884
+ const iconRegex = [
1885
+ /^icon-([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/,
1886
+ // icon-16.png
1887
+ /^icon-([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/,
1888
+ // icon-16x16.png
1889
+ /^icon@([0-9]+)w\.(png|bmp|jpeg|jpg|ico|gif)$/,
1890
+ // icon@16w.png
1891
+ /^icon@([0-9]+)h\.(png|bmp|jpeg|jpg|ico|gif)$/,
1892
+ // icon@16h.png
1893
+ /^icon@([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/,
1894
+ // icon@16.png
1895
+ /^icon[\/\\]([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/,
1896
+ // icon/16.png
1897
+ /^icon[\/\\]([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/
1898
+ // icon/16x16.png
1899
+ ];
1900
+ buildOutput.publicAssets.forEach((asset) => {
1901
+ let size;
1902
+ for (const regex of iconRegex) {
1903
+ const match = asset.fileName.match(regex);
1904
+ if (match?.[1] != null) {
1905
+ size = match[1];
1906
+ break;
1907
+ }
1908
+ }
1909
+ if (size == null)
1910
+ return;
1911
+ icons.push([size, normalizePath2(asset.fileName)]);
1912
+ });
1913
+ return icons.length > 0 ? Object.fromEntries(icons) : void 0;
1914
+ }
1915
+ function addDevModeCsp(manifest, config) {
1916
+ const permission = `http://${config.server?.hostname ?? ""}/*`;
1917
+ const allowedCsp = config.server?.origin ?? "http://localhost:*";
1918
+ if (manifest.manifest_version === 3) {
1919
+ addHostPermission(manifest, permission);
1920
+ } else {
1921
+ addPermission(manifest, permission);
1922
+ }
1923
+ const csp = new ContentSecurityPolicy(
1924
+ manifest.manifest_version === 3 ? (
1925
+ // @ts-expect-error: extension_pages is not typed
1926
+ manifest.content_security_policy?.extension_pages ?? "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
1927
+ ) : manifest.content_security_policy ?? "script-src 'self'; object-src 'self';"
1928
+ // default CSP for MV2
1929
+ );
1930
+ if (config.server)
1931
+ csp.add("script-src", allowedCsp);
1932
+ if (manifest.manifest_version === 3) {
1933
+ manifest.content_security_policy ??= {};
1934
+ manifest.content_security_policy.extension_pages = csp.toString();
1935
+ } else {
1936
+ manifest.content_security_policy = csp.toString();
1937
+ }
1938
+ }
1939
+ function addDevModePermissions(manifest, config) {
1940
+ addPermission(manifest, "tabs");
1941
+ if (config.manifestVersion === 3)
1942
+ addPermission(manifest, "scripting");
1943
+ }
1944
+ function getContentScriptCssFiles(contentScripts, contentScriptCssMap) {
1945
+ const css = [];
1946
+ contentScripts.forEach((script) => {
1947
+ if (script.options.cssInjectionMode === "manual" || script.options.cssInjectionMode === "ui")
1948
+ return;
1949
+ const cssFile = contentScriptCssMap[script.name];
1950
+ if (cssFile == null)
1951
+ return;
1952
+ if (cssFile)
1953
+ css.push(cssFile);
1954
+ });
1955
+ if (css.length > 0)
1956
+ return css;
1957
+ return void 0;
1958
+ }
1959
+ function getContentScriptCssWebAccessibleResources(config, contentScripts, contentScriptCssMap) {
1960
+ const resources = [];
1961
+ contentScripts.forEach((script) => {
1962
+ if (script.options.cssInjectionMode !== "ui")
1963
+ return;
1964
+ const cssFile = contentScriptCssMap[script.name];
1965
+ if (cssFile == null)
1966
+ return;
1967
+ if (config.manifestVersion === 2) {
1968
+ resources.push(cssFile);
1969
+ } else {
1970
+ resources.push({
1971
+ resources: [cssFile],
1972
+ matches: script.options.matches
1973
+ });
1974
+ }
1975
+ });
1976
+ return resources;
1977
+ }
1978
+ function getContentScriptsCssMap(buildOutput, scripts) {
1979
+ const map = {};
1980
+ const allChunks = buildOutput.steps.flatMap((step) => step.chunks);
1981
+ scripts.forEach((script) => {
1982
+ const relatedCss = allChunks.find(
1983
+ (chunk) => chunk.fileName === `content-scripts/${script.name}.css`
1984
+ );
1985
+ if (relatedCss != null)
1986
+ map[script.name] = relatedCss.fileName;
1987
+ });
1988
+ return map;
1989
+ }
1990
+ function addPermission(manifest, permission) {
1991
+ manifest.permissions ??= [];
1992
+ if (manifest.permissions.includes(permission))
1993
+ return;
1994
+ manifest.permissions.push(permission);
1995
+ }
1996
+ function addHostPermission(manifest, hostPermission) {
1997
+ manifest.host_permissions ??= [];
1998
+ if (manifest.host_permissions.includes(hostPermission))
1999
+ return;
2000
+ manifest.host_permissions.push(hostPermission);
2001
+ }
2002
+
2003
+ // src/core/utils/building/rebuild.ts
2004
+ async function rebuild(config, entrypointGroups, existingOutput = {
2005
+ steps: [],
2006
+ publicAssets: []
2007
+ }) {
2008
+ const { default: ora } = await import("ora");
2009
+ const spinner = ora(`Preparing...`).start();
2010
+ const allEntrypoints = await findEntrypoints(config);
2011
+ await generateTypesDir(allEntrypoints, config).catch((err) => {
2012
+ config.logger.warn("Failed to update .wxt directory:", err);
2013
+ if (config.command === "build")
2014
+ throw err;
2015
+ });
2016
+ const newOutput = await buildEntrypoints(entrypointGroups, config, spinner);
2017
+ const mergedOutput = {
2018
+ steps: [...existingOutput.steps, ...newOutput.steps],
2019
+ publicAssets: [...existingOutput.publicAssets, ...newOutput.publicAssets]
2020
+ };
2021
+ const newManifest = await generateMainfest(
2022
+ allEntrypoints,
2023
+ mergedOutput,
2024
+ config
2025
+ );
2026
+ const finalOutput = {
2027
+ manifest: newManifest,
2028
+ ...newOutput
2029
+ };
2030
+ await writeManifest(newManifest, finalOutput, config);
2031
+ spinner.clear().stop();
2032
+ return {
2033
+ output: {
2034
+ manifest: newManifest,
2035
+ steps: [...existingOutput.steps, ...finalOutput.steps],
2036
+ publicAssets: [
2037
+ ...existingOutput.publicAssets,
2038
+ ...finalOutput.publicAssets
2039
+ ]
2040
+ },
2041
+ manifest: newManifest
2042
+ };
2043
+ }
2044
+
2045
+ // src/core/utils/building/internal-build.ts
2046
+ async function internalBuild(config) {
2047
+ const verb = config.command === "serve" ? "Pre-rendering" : "Building";
2048
+ const target = `${config.browser}-mv${config.manifestVersion}`;
2049
+ config.logger.info(
2050
+ `${verb} ${pc4.cyan(target)} for ${pc4.cyan(config.mode)} with ${pc4.green(
2051
+ `Vite ${vite5.version}`
2052
+ )}`
2053
+ );
2054
+ const startTime = Date.now();
2055
+ await fs11.rm(config.outDir, { recursive: true, force: true });
2056
+ await fs11.ensureDir(config.outDir);
2057
+ const entrypoints = await findEntrypoints(config);
2058
+ config.logger.debug("Detected entrypoints:", entrypoints);
2059
+ const groups = groupEntrypoints(entrypoints);
2060
+ const { output } = await rebuild(config, groups, void 0);
2061
+ await printBuildSummary(
2062
+ config.logger.success,
2063
+ `Built extension in ${formatDuration(Date.now() - startTime)}`,
2064
+ output,
2065
+ config
2066
+ );
2067
+ if (config.analysis.enabled) {
2068
+ await combineAnalysisStats(config);
2069
+ config.logger.info(
2070
+ `Analysis complete:
2071
+ ${pc4.gray("\u2514\u2500")} ${pc4.yellow("stats.html")}`
2072
+ );
2073
+ }
2074
+ return output;
2075
+ }
2076
+ async function combineAnalysisStats(config) {
2077
+ const { execaCommand } = await import("./execa-WKZHVHC5.js");
2078
+ const unixFiles = await glob2(`stats-*.json`, {
2079
+ cwd: config.outDir,
2080
+ absolute: true
2081
+ });
2082
+ const absolutePaths = unixFiles.map(unnormalizePath);
2083
+ await execaCommand(
2084
+ `rollup-plugin-visualizer ${absolutePaths.join(" ")} --template ${config.analysis.template}`,
2085
+ { cwd: config.root, stdio: "inherit" }
2086
+ );
2087
+ }
2088
+
2089
+ // src/core/utils/building/find-entrypoints.ts
2090
+ import glob3 from "fast-glob";
2091
+ async function findEntrypoints(config) {
2092
+ const relativePaths = await glob3("**/*", {
2093
+ cwd: config.entrypointsDir
2094
+ });
2095
+ relativePaths.sort();
2096
+ const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
2097
+ let hasBackground = false;
2098
+ const possibleEntrypoints = await Promise.all(
2099
+ relativePaths.map(async (relativePath) => {
2100
+ const path7 = resolve12(config.entrypointsDir, relativePath);
2101
+ const matchingGlob = pathGlobs.find(
2102
+ (glob4) => minimatch(relativePath, glob4)
2103
+ );
2104
+ if (matchingGlob == null) {
2105
+ config.logger.warn(
2106
+ `${relativePath} does not match any known entrypoint. Known entrypoints:
2107
+ ${JSON.stringify(
2108
+ PATH_GLOB_TO_TYPE_MAP,
2109
+ null,
2110
+ 2
2111
+ )}`
2112
+ );
2113
+ return;
2114
+ }
2115
+ const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
2116
+ if (type === "ignored")
2117
+ return;
2118
+ switch (type) {
2119
+ case "popup":
2120
+ return await getPopupEntrypoint(config, path7);
2121
+ case "options":
2122
+ return await getOptionsEntrypoint(config, path7);
2123
+ case "background":
2124
+ hasBackground = true;
2125
+ return await getBackgroundEntrypoint(config, path7);
2126
+ case "content-script":
2127
+ return await getContentScriptEntrypoint(config, path7);
2128
+ case "unlisted-page":
2129
+ return await getUnlistedPageEntrypoint(config, path7);
2130
+ case "unlisted-script":
2131
+ return await getUnlistedScriptEntrypoint(config, path7);
2132
+ case "content-script-style":
2133
+ return {
2134
+ type,
2135
+ name: getEntrypointName(config.entrypointsDir, path7),
2136
+ inputPath: path7,
2137
+ outputDir: resolve12(config.outDir, CONTENT_SCRIPT_OUT_DIR),
2138
+ options: {
2139
+ include: void 0,
2140
+ exclude: void 0
2141
+ }
2142
+ };
2143
+ default:
2144
+ return {
2145
+ type,
2146
+ name: getEntrypointName(config.entrypointsDir, path7),
2147
+ inputPath: path7,
2148
+ outputDir: config.outDir,
2149
+ options: {
2150
+ include: void 0,
2151
+ exclude: void 0
2152
+ }
2153
+ };
2154
+ }
2155
+ })
2156
+ );
2157
+ const entrypoints = possibleEntrypoints.filter(
2158
+ (entry) => !!entry
2159
+ );
2160
+ const existingNames = {};
2161
+ entrypoints.forEach((entrypoint) => {
2162
+ const withSameName = existingNames[entrypoint.name];
2163
+ if (withSameName) {
2164
+ throw Error(
2165
+ `Multiple entrypoints with the name "${entrypoint.name}" detected, but only one is allowed: ${[
2166
+ relative4(config.root, withSameName.inputPath),
2167
+ relative4(config.root, entrypoint.inputPath)
2168
+ ].join(", ")}`
2169
+ );
2170
+ }
2171
+ existingNames[entrypoint.name] = entrypoint;
2172
+ });
2173
+ if (config.command === "serve" && !hasBackground) {
2174
+ entrypoints.push(
2175
+ await getBackgroundEntrypoint(config, VIRTUAL_NOOP_BACKGROUND_MODULE_ID)
2176
+ );
2177
+ }
2178
+ config.logger.debug("All entrypoints:", entrypoints);
2179
+ const targetEntrypoints = entrypoints.filter((entry) => {
2180
+ const { include, exclude } = entry.options;
2181
+ if (include?.length && exclude?.length) {
2182
+ config.logger.warn(
2183
+ `The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`
2184
+ );
2185
+ return false;
2186
+ }
2187
+ if (exclude?.length && !include?.length) {
2188
+ return !exclude.includes(config.browser);
2189
+ }
2190
+ if (include?.length && !exclude?.length) {
2191
+ return include.includes(config.browser);
2192
+ }
2193
+ return true;
2194
+ });
2195
+ config.logger.debug(`${config.browser} entrypoints:`, targetEntrypoints);
2196
+ return targetEntrypoints;
2197
+ }
2198
+ function getHtmlBaseOptions(document) {
2199
+ const options = {};
2200
+ const includeContent = document.querySelector("meta[name='manifest.include']")?.getAttribute("content");
2201
+ if (includeContent) {
2202
+ options.include = JSON5.parse(includeContent);
2203
+ }
2204
+ const excludeContent = document.querySelector("meta[name='manifest.exclude']")?.getAttribute("content");
2205
+ if (excludeContent) {
2206
+ options.exclude = JSON5.parse(excludeContent);
2207
+ }
2208
+ return options;
2209
+ }
2210
+ async function getPopupEntrypoint(config, path7) {
2211
+ const content = await fs12.readFile(path7, "utf-8");
2212
+ const { document } = parseHTML2(content);
2213
+ const options = getHtmlBaseOptions(document);
2214
+ const title = document.querySelector("title");
2215
+ if (title != null)
2216
+ options.defaultTitle = title.textContent ?? void 0;
2217
+ const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
2218
+ if (defaultIconContent) {
2219
+ try {
2220
+ options.defaultIcon = JSON5.parse(defaultIconContent);
2221
+ } catch (err) {
2222
+ config.logger.fatal(
2223
+ `Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
2224
+ err
2225
+ );
2226
+ }
2227
+ }
2228
+ const mv2TypeContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
2229
+ if (mv2TypeContent) {
2230
+ options.mv2Key = mv2TypeContent === "page_action" ? "page_action" : "browser_action";
2231
+ }
2232
+ const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
2233
+ if (browserStyleContent) {
2234
+ options.browserStyle = browserStyleContent === "true";
2235
+ }
2236
+ return {
2237
+ type: "popup",
2238
+ name: "popup",
2239
+ options,
2240
+ inputPath: path7,
2241
+ outputDir: config.outDir
2242
+ };
2243
+ }
2244
+ async function getOptionsEntrypoint(config, path7) {
2245
+ const content = await fs12.readFile(path7, "utf-8");
2246
+ const { document } = parseHTML2(content);
2247
+ const options = getHtmlBaseOptions(document);
2248
+ const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
2249
+ if (openInTabContent) {
2250
+ options.openInTab = openInTabContent === "true";
2251
+ }
2252
+ const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
2253
+ if (chromeStyleContent) {
2254
+ options.chromeStyle = chromeStyleContent === "true";
2255
+ }
2256
+ const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
2257
+ if (browserStyleContent) {
2258
+ options.browserStyle = browserStyleContent === "true";
2259
+ }
2260
+ return {
2261
+ type: "options",
2262
+ name: "options",
2263
+ options,
2264
+ inputPath: path7,
2265
+ outputDir: config.outDir
2266
+ };
2267
+ }
2268
+ async function getUnlistedPageEntrypoint(config, path7) {
2269
+ const content = await fs12.readFile(path7, "utf-8");
2270
+ const { document } = parseHTML2(content);
2271
+ return {
2272
+ type: "unlisted-page",
2273
+ name: getEntrypointName(config.entrypointsDir, path7),
2274
+ inputPath: path7,
2275
+ outputDir: config.outDir,
2276
+ options: getHtmlBaseOptions(document)
2277
+ };
2278
+ }
2279
+ async function getUnlistedScriptEntrypoint(config, path7) {
2280
+ const name = getEntrypointName(config.entrypointsDir, path7);
2281
+ const defaultExport = await importEntrypointFile(
2282
+ path7,
2283
+ config
2284
+ );
2285
+ if (defaultExport == null) {
2286
+ throw Error(
2287
+ `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
2288
+ );
2289
+ }
2290
+ const { main: _, ...moduleOptions } = defaultExport;
2291
+ const options = moduleOptions;
2292
+ return {
2293
+ type: "unlisted-script",
2294
+ name,
2295
+ inputPath: path7,
2296
+ outputDir: config.outDir,
2297
+ options
2298
+ };
2299
+ }
2300
+ async function getBackgroundEntrypoint(config, path7) {
2301
+ const name = "background";
2302
+ let options = {};
2303
+ if (path7 !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
2304
+ const defaultExport = await importEntrypointFile(
2305
+ path7,
2306
+ config
2307
+ );
2308
+ if (defaultExport == null) {
2309
+ throw Error(
2310
+ `${name}: Default export not found, did you forget to call "export default defineBackground(...)"?`
2311
+ );
2312
+ }
2313
+ const { main: _, ...moduleOptions } = defaultExport;
2314
+ options = moduleOptions;
2315
+ }
2316
+ return {
2317
+ type: "background",
2318
+ name,
2319
+ inputPath: path7,
2320
+ outputDir: config.outDir,
2321
+ options: {
2322
+ ...options,
2323
+ type: resolvePerBrowserOption(options.type, config.browser),
2324
+ persistent: resolvePerBrowserOption(options.persistent, config.browser)
2325
+ }
2326
+ };
2327
+ }
2328
+ async function getContentScriptEntrypoint(config, path7) {
2329
+ const name = getEntrypointName(config.entrypointsDir, path7);
2330
+ const { main: _, ...options } = await importEntrypointFile(path7, config);
2331
+ if (options == null) {
2332
+ throw Error(
2333
+ `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
2334
+ );
2335
+ }
2336
+ return {
2337
+ type: "content-script",
2338
+ name,
2339
+ inputPath: path7,
2340
+ outputDir: resolve12(config.outDir, CONTENT_SCRIPT_OUT_DIR),
2341
+ options
2342
+ };
2343
+ }
2344
+ var PATH_GLOB_TO_TYPE_MAP = {
2345
+ "sandbox.html": "sandbox",
2346
+ "sandbox/index.html": "sandbox",
2347
+ "*.sandbox.html": "sandbox",
2348
+ "*.sandbox/index.html": "sandbox",
2349
+ "bookmarks.html": "bookmarks",
2350
+ "bookmarks/index.html": "bookmarks",
2351
+ "history.html": "history",
2352
+ "history/index.html": "history",
2353
+ "newtab.html": "newtab",
2354
+ "newtab/index.html": "newtab",
2355
+ "sidepanel.html": "sidepanel",
2356
+ "sidepanel/index.html": "sidepanel",
2357
+ "*.sidepanel.html": "sidepanel",
2358
+ "*.sidepanel/index.html": "sidepanel",
2359
+ "devtools.html": "devtools",
2360
+ "devtools/index.html": "devtools",
2361
+ "background.[jt]s": "background",
2362
+ "background/index.[jt]s": "background",
2363
+ [VIRTUAL_NOOP_BACKGROUND_MODULE_ID]: "background",
2364
+ "content.[jt]s?(x)": "content-script",
2365
+ "content/index.[jt]s?(x)": "content-script",
2366
+ "*.content.[jt]s?(x)": "content-script",
2367
+ "*.content/index.[jt]s?(x)": "content-script",
2368
+ [`content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2369
+ [`*.content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2370
+ [`content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2371
+ [`*.content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
2372
+ "popup.html": "popup",
2373
+ "popup/index.html": "popup",
2374
+ "options.html": "options",
2375
+ "options/index.html": "options",
2376
+ "*.html": "unlisted-page",
2377
+ "*/index.html": "unlisted-page",
2378
+ "*.[jt]s": "unlisted-script",
2379
+ "*/index.ts": "unlisted-script",
2380
+ [`*.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
2381
+ [`*/index.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
2382
+ // Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files or tests
2383
+ "*/**": "ignored"
2384
+ };
2385
+ var CONTENT_SCRIPT_OUT_DIR = "content-scripts";
2386
+
2387
+ export {
2388
+ getEntrypointOutputFile,
2389
+ getEntrypointBundlePath,
2390
+ resolvePerBrowserOption,
2391
+ formatDuration,
2392
+ download,
2393
+ unimport,
2394
+ tsconfigPaths,
2395
+ globals,
2396
+ webextensionPolyfillAlias,
2397
+ webextensionPolyfillInlineDeps,
2398
+ detectDevChanges,
2399
+ findEntrypoints,
2400
+ generateTypesDir,
2401
+ getInternalConfig,
2402
+ kebabCaseAlphanumeric,
2403
+ printFileList,
2404
+ version,
2405
+ getPackageJson,
2406
+ getContentScriptCssFiles,
2407
+ getContentScriptsCssMap,
2408
+ rebuild,
2409
+ internalBuild
2410
+ };