wxt 0.8.3 → 0.8.5

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