wxt 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1441 @@
1
+ // src/core/utils/getInternalConfig.ts
2
+ import path2, { resolve as resolve7 } from "node:path";
3
+ import * as vite from "vite";
4
+ import { consola as consola2 } from "consola";
5
+
6
+ // src/core/utils/importTsFile.ts
7
+ import { consola } from "consola";
8
+ import createJITI from "jiti";
9
+ import transform from "jiti/dist/babel";
10
+ import { resolve } from "path";
11
+ import { scanExports } from "unimport";
12
+ async function importTsFile(root, path5) {
13
+ const clientImports = await scanExports(
14
+ resolve(root, "node_modules/wxt/dist/client/index.js")
15
+ );
16
+ const jiti = createJITI(__filename, {
17
+ cache: false,
18
+ esmResolve: true,
19
+ interopDefault: true,
20
+ transform(opts) {
21
+ opts.source = opts.source.replace(/^import ['"].*\.css['"];?$/gm, "");
22
+ opts.source = opts.source.replace(
23
+ /^import\s+.*\s+from ['"]webextension-polyfill['"];?$/gm,
24
+ ""
25
+ );
26
+ if (opts.filename === path5) {
27
+ const imports = clientImports.map((i) => `import { ${i.name} } from "${i.from}";`).join("\n") + "\n";
28
+ opts.source = imports + opts.source;
29
+ }
30
+ return transform(opts);
31
+ }
32
+ });
33
+ try {
34
+ return await jiti(path5);
35
+ } catch (err) {
36
+ consola.error(`Failed to import file: ${path5}`);
37
+ throw err;
38
+ }
39
+ }
40
+
41
+ // src/core/utils/network.ts
42
+ import dns from "node:dns";
43
+
44
+ // src/core/utils/promises.ts
45
+ function withTimeout(promise, duration) {
46
+ return new Promise((res, rej) => {
47
+ const timeout = setTimeout(() => {
48
+ rej(`Promise timed out after ${duration}ms`);
49
+ }, duration);
50
+ promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
51
+ });
52
+ }
53
+
54
+ // src/core/utils/network.ts
55
+ function isOffline() {
56
+ const isOffline2 = new Promise((res) => {
57
+ dns.resolve("google.com", (err) => {
58
+ if (err == null) {
59
+ res(false);
60
+ } else {
61
+ res(true);
62
+ }
63
+ });
64
+ });
65
+ return withTimeout(isOffline2, 1e3).catch(() => true);
66
+ }
67
+ async function isOnline() {
68
+ const offline = await isOffline();
69
+ return !offline;
70
+ }
71
+ async function fetchCached(url, config) {
72
+ let content = "";
73
+ if (await isOnline()) {
74
+ const res = await fetch(url);
75
+ if (res.status < 300) {
76
+ content = await res.text();
77
+ await config.fsCache.set(url, content);
78
+ } else {
79
+ config.logger.debug(
80
+ `Failed to download "${url}", falling back to cache...`
81
+ );
82
+ }
83
+ }
84
+ if (!content)
85
+ content = await config.fsCache.get(url) ?? "";
86
+ if (!content)
87
+ throw Error(
88
+ `Offline and "${url}" has not been cached. Try again when online.`
89
+ );
90
+ return content;
91
+ }
92
+
93
+ // src/core/vite-plugins/download.ts
94
+ function download(config) {
95
+ return {
96
+ name: "wxt:download",
97
+ resolveId(id) {
98
+ if (id.startsWith("url:"))
99
+ return "\0" + id;
100
+ },
101
+ async load(id) {
102
+ if (!id.startsWith("\0url:"))
103
+ return;
104
+ const url = id.replace("\0url:", "");
105
+ return await fetchCached(url, config);
106
+ }
107
+ };
108
+ }
109
+
110
+ // src/core/vite-plugins/unimport.ts
111
+ import { createUnimport } from "unimport";
112
+
113
+ // src/core/utils/auto-imports.ts
114
+ import { mergeConfig } from "vite";
115
+ function getUnimportOptions(config) {
116
+ const defaultOptions = {
117
+ debugLog: config.logger.debug,
118
+ imports: [
119
+ { name: "*", as: "browser", from: "webextension-polyfill" },
120
+ { name: "defineConfig", from: "wxt" }
121
+ ],
122
+ presets: [{ package: "wxt/client" }],
123
+ warn: config.logger.warn,
124
+ dirs: ["components", "composables", "hooks", "utils"]
125
+ };
126
+ return mergeConfig(
127
+ defaultOptions,
128
+ config.imports
129
+ );
130
+ }
131
+
132
+ // src/core/vite-plugins/unimport.ts
133
+ function unimport(config) {
134
+ const options = getUnimportOptions(config);
135
+ const unimport2 = createUnimport(options);
136
+ return {
137
+ name: "wxt:unimport",
138
+ async config() {
139
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
140
+ },
141
+ async transform(code, id) {
142
+ return unimport2.injectImports(code, id);
143
+ }
144
+ };
145
+ }
146
+
147
+ // src/core/vite-plugins/multipageMove.ts
148
+ import { dirname, extname, resolve as resolve3 } from "node:path";
149
+
150
+ // src/core/utils/entrypoints.ts
151
+ import path, { relative, resolve as resolve2 } from "node:path";
152
+ function getEntrypointName(entrypointsDir, inputPath) {
153
+ const relativePath = path.relative(entrypointsDir, inputPath);
154
+ const name = relativePath.split(/[\.\/]/, 2)[0];
155
+ return name;
156
+ }
157
+ function getEntrypointOutputFile(entrypoint, ext) {
158
+ return resolve2(entrypoint.outputDir, `${entrypoint.name}${ext}`);
159
+ }
160
+ function getEntrypointBundlePath(entrypoint, outDir, ext) {
161
+ return relative(outDir, getEntrypointOutputFile(entrypoint, ext));
162
+ }
163
+
164
+ // src/core/vite-plugins/multipageMove.ts
165
+ import fs, { ensureDir } from "fs-extra";
166
+ function multipageMove(entrypoints, config) {
167
+ return {
168
+ name: "wxt:multipage-move",
169
+ async writeBundle(_, bundle) {
170
+ for (const oldBundlePath in bundle) {
171
+ const entrypoint = entrypoints.find(
172
+ (entry) => !!entry.inputPath.endsWith(oldBundlePath)
173
+ );
174
+ if (entrypoint == null) {
175
+ config.logger.debug("No entrypoint found for", oldBundlePath);
176
+ continue;
177
+ }
178
+ const newBundlePath = getEntrypointBundlePath(
179
+ entrypoint,
180
+ config.outDir,
181
+ extname(oldBundlePath)
182
+ );
183
+ if (newBundlePath === oldBundlePath) {
184
+ config.logger.debug(
185
+ "HTML file is already in the correct location",
186
+ oldBundlePath
187
+ );
188
+ continue;
189
+ }
190
+ const oldAbsPath = resolve3(config.outDir, oldBundlePath);
191
+ const newAbsPath = resolve3(config.outDir, newBundlePath);
192
+ await ensureDir(dirname(newAbsPath));
193
+ await fs.move(oldAbsPath, newAbsPath);
194
+ const renamedChunk = {
195
+ ...bundle[oldBundlePath],
196
+ fileName: newBundlePath
197
+ };
198
+ delete bundle[oldBundlePath];
199
+ bundle[newBundlePath] = renamedChunk;
200
+ }
201
+ }
202
+ };
203
+ }
204
+
205
+ // src/core/vite-plugins/devHtmlPrerender.ts
206
+ import { parseHTML } from "linkedom";
207
+ import { dirname as dirname2, isAbsolute, relative as relative2, resolve as resolve4 } from "path";
208
+ function devHtmlPrerender(config) {
209
+ return {
210
+ apply: "build",
211
+ name: "wxt:dev-html-prerender",
212
+ async transform(html, id) {
213
+ const server = config.server;
214
+ if (config.command !== "serve" || server == null || !id.endsWith(".html"))
215
+ return;
216
+ const originalUrl = `${server.origin}${id}`;
217
+ const name = getEntrypointName(config.entrypointsDir, id);
218
+ const url = `${server.origin}/${name}.html`;
219
+ const serverHtml = await server.transformIndexHtml(
220
+ url,
221
+ html,
222
+ originalUrl
223
+ );
224
+ const { document } = parseHTML(serverHtml);
225
+ const pointToDevServer = (querySelector, attr) => {
226
+ document.querySelectorAll(querySelector).forEach((element) => {
227
+ const src = element.getAttribute(attr);
228
+ if (!src)
229
+ return;
230
+ if (isAbsolute(src)) {
231
+ element.setAttribute(attr, server.origin + src);
232
+ } else if (src.startsWith(".")) {
233
+ const abs = resolve4(dirname2(id), src);
234
+ const pathname = relative2(config.root, abs);
235
+ element.setAttribute(attr, `${server.origin}/${pathname}`);
236
+ }
237
+ });
238
+ };
239
+ pointToDevServer("script[type=module]", "src");
240
+ pointToDevServer("link[rel=stylesheet]", "href");
241
+ const newHtml = document.toString();
242
+ config.logger.debug("Transformed " + id);
243
+ config.logger.debug("Old HTML:\n" + html);
244
+ config.logger.debug("New HTML:\n" + newHtml);
245
+ return newHtml;
246
+ }
247
+ };
248
+ }
249
+
250
+ // src/core/vite-plugins/virtualEntrypoint.ts
251
+ import fs2 from "fs-extra";
252
+ import { resolve as resolve5 } from "path";
253
+ function virtualEntrypoin(type, config) {
254
+ const virtualId = `virtual:wxt-${type}?`;
255
+ const resolvedVirtualId = `\0${virtualId}`;
256
+ return {
257
+ name: `wxt:virtual-entrypoint`,
258
+ resolveId(id) {
259
+ const index = id.indexOf(virtualId);
260
+ if (index === -1)
261
+ return;
262
+ const inputPath = id.substring(index + virtualId.length);
263
+ return resolvedVirtualId + inputPath;
264
+ },
265
+ async load(id) {
266
+ if (!id.startsWith(resolvedVirtualId))
267
+ return;
268
+ const inputPath = id.replace(resolvedVirtualId, "");
269
+ const template = await fs2.readFile(
270
+ resolve5(config.root, `node_modules/wxt/templates/virtual-${type}.ts`),
271
+ "utf-8"
272
+ );
273
+ return template.replaceAll("{{moduleId}}", inputPath);
274
+ }
275
+ };
276
+ }
277
+
278
+ // src/core/utils/createFsCache.ts
279
+ import fs3, { ensureDir as ensureDir2 } from "fs-extra";
280
+ import { dirname as dirname3, resolve as resolve6 } from "path";
281
+ function createFsCache(wxtDir) {
282
+ const getPath = (key) => resolve6(wxtDir, "cache", encodeURIComponent(key));
283
+ return {
284
+ async set(key, value) {
285
+ const path5 = getPath(key);
286
+ await ensureDir2(dirname3(path5));
287
+ await fs3.writeFile(path5, value, "utf-8");
288
+ },
289
+ async get(key) {
290
+ const path5 = getPath(key);
291
+ try {
292
+ return await fs3.readFile(path5, "utf-8");
293
+ } catch {
294
+ return void 0;
295
+ }
296
+ }
297
+ };
298
+ }
299
+
300
+ // src/core/utils/globals.ts
301
+ function getGlobals(config) {
302
+ return [
303
+ {
304
+ name: "__MANIFEST_VERSION__",
305
+ value: config.manifestVersion,
306
+ type: `2 | 3`
307
+ },
308
+ {
309
+ name: "__BROWSER__",
310
+ value: config.browser,
311
+ type: `"chromium" | "firefox"`
312
+ },
313
+ {
314
+ name: "__IS_CHROME__",
315
+ value: config.browser === "chrome",
316
+ type: `boolean`
317
+ },
318
+ {
319
+ name: "__IS_FIREFOX__",
320
+ value: config.browser === "firefox",
321
+ type: `boolean`
322
+ },
323
+ {
324
+ name: "__IS_SAFARI__",
325
+ value: config.browser === "safari",
326
+ type: `boolean`
327
+ },
328
+ {
329
+ name: "__IS_EDGE__",
330
+ value: config.browser === "edge",
331
+ type: `boolean`
332
+ },
333
+ {
334
+ name: "__IS_OPERA__",
335
+ value: config.browser === "opera",
336
+ type: `boolean`
337
+ }
338
+ ];
339
+ }
340
+
341
+ // src/core/utils/getInternalConfig.ts
342
+ import { loadConfig } from "c12";
343
+ async function getInternalConfig(config, command) {
344
+ const root = config.root ? path2.resolve(config.root) : process.cwd();
345
+ const mode = config.mode ?? (command === "build" ? "production" : "development");
346
+ const browser = config.browser ?? "chrome";
347
+ const manifestVersion = config.manifestVersion ?? (browser == "firefox" ? 2 : 3);
348
+ const outBaseDir = path2.resolve(root, ".output");
349
+ const outDir = path2.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
350
+ const logger = config.logger ?? consola2;
351
+ const baseConfig = {
352
+ root,
353
+ outDir,
354
+ outBaseDir,
355
+ storeIds: config.storeIds ?? {},
356
+ browser,
357
+ manifestVersion,
358
+ mode,
359
+ command,
360
+ logger,
361
+ vite: config.vite ?? {},
362
+ manifest: config.manifest ?? {},
363
+ imports: config.imports ?? {},
364
+ runnerConfig: await loadConfig({
365
+ name: "web-ext",
366
+ cwd: root,
367
+ globalRc: true,
368
+ rcFile: ".webextrc",
369
+ overrides: config.runner
370
+ })
371
+ };
372
+ let userConfig = {
373
+ mode
374
+ };
375
+ if (config.configFile !== false) {
376
+ userConfig = await importTsFile(
377
+ root,
378
+ path2.resolve(root, config.configFile ?? "wxt.config.ts")
379
+ );
380
+ }
381
+ const merged = vite.mergeConfig(
382
+ baseConfig,
383
+ userConfig
384
+ );
385
+ const srcDir = userConfig.srcDir ? resolve7(root, userConfig.srcDir) : root;
386
+ const entrypointsDir = resolve7(
387
+ srcDir,
388
+ userConfig.entrypointsDir ?? "entrypoints"
389
+ );
390
+ const publicDir = resolve7(srcDir, userConfig.publicDir ?? "public");
391
+ const wxtDir = resolve7(srcDir, ".wxt");
392
+ const typesDir = resolve7(wxtDir, "types");
393
+ const finalConfig = {
394
+ ...merged,
395
+ srcDir,
396
+ entrypointsDir,
397
+ publicDir,
398
+ wxtDir,
399
+ typesDir,
400
+ fsCache: createFsCache(wxtDir)
401
+ };
402
+ finalConfig.vite.root = root;
403
+ finalConfig.vite.configFile = false;
404
+ finalConfig.vite.logLevel = "silent";
405
+ finalConfig.vite.build ??= {};
406
+ finalConfig.vite.build.outDir = outDir;
407
+ finalConfig.vite.build.emptyOutDir = false;
408
+ finalConfig.vite.plugins ??= [];
409
+ finalConfig.vite.plugins.push(download(finalConfig));
410
+ finalConfig.vite.plugins.push(devHtmlPrerender(finalConfig));
411
+ finalConfig.vite.plugins.push(unimport(finalConfig));
412
+ finalConfig.vite.plugins.push(
413
+ virtualEntrypoin("background", finalConfig)
414
+ );
415
+ finalConfig.vite.plugins.push(
416
+ virtualEntrypoin("content-script", finalConfig)
417
+ );
418
+ finalConfig.vite.define ??= {};
419
+ getGlobals(finalConfig).forEach((global) => {
420
+ finalConfig.vite.define[global.name] = JSON.stringify(global.value);
421
+ });
422
+ return finalConfig;
423
+ }
424
+
425
+ // src/core/build/findEntrypoints.ts
426
+ import { relative as relative3, resolve as resolve8 } from "path";
427
+ import fs4 from "fs-extra";
428
+ import picomatch from "picomatch";
429
+ import { parseHTML as parseHTML2 } from "linkedom";
430
+ import JSON5 from "json5";
431
+ import glob from "fast-glob";
432
+ async function findEntrypoints(config) {
433
+ const relativePaths = await glob("**/*", {
434
+ cwd: config.entrypointsDir
435
+ });
436
+ relativePaths.sort();
437
+ const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
438
+ const existingNames = {};
439
+ const entrypoints = [];
440
+ await Promise.all(
441
+ relativePaths.map(async (relativePath) => {
442
+ const path5 = resolve8(config.entrypointsDir, relativePath);
443
+ const matchingGlob = pathGlobs.find(
444
+ (glob3) => picomatch.isMatch(relativePath, glob3)
445
+ );
446
+ if (matchingGlob == null) {
447
+ return config.logger.warn(
448
+ `${relativePath} does not match any known entrypoint. Known entrypoints:
449
+ ${JSON.stringify(
450
+ PATH_GLOB_TO_TYPE_MAP,
451
+ null,
452
+ 2
453
+ )}`
454
+ );
455
+ }
456
+ const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
457
+ if (type === "ignored")
458
+ return;
459
+ let entrypoint;
460
+ switch (type) {
461
+ case "popup":
462
+ entrypoint = await getPopupEntrypoint(config, path5);
463
+ break;
464
+ case "options":
465
+ entrypoint = await getOptionsEntrypoint(config, path5);
466
+ break;
467
+ case "background":
468
+ entrypoint = await getBackgroundEntrypoint(config, path5);
469
+ break;
470
+ case "content-script":
471
+ entrypoint = await getContentScriptEntrypoint(
472
+ config,
473
+ relativePath.split(".", 2)[0],
474
+ path5
475
+ );
476
+ break;
477
+ default:
478
+ entrypoint = {
479
+ type,
480
+ name: getEntrypointName(config.entrypointsDir, path5),
481
+ inputPath: path5,
482
+ outputDir: config.outDir
483
+ };
484
+ }
485
+ const withSameName = existingNames[entrypoint.name];
486
+ if (withSameName) {
487
+ throw Error(
488
+ `Multiple entrypoints with the name "${entrypoint.name}" detected, but only one is allowed: ${[
489
+ relative3(config.root, withSameName.inputPath),
490
+ relative3(config.root, entrypoint.inputPath)
491
+ ].join(", ")}`
492
+ );
493
+ }
494
+ entrypoints.push(entrypoint);
495
+ existingNames[entrypoint.name] = entrypoint;
496
+ })
497
+ );
498
+ return entrypoints;
499
+ }
500
+ async function getPopupEntrypoint(config, path5) {
501
+ const options = {};
502
+ const content = await fs4.readFile(path5, "utf-8");
503
+ const { document } = parseHTML2(content);
504
+ const title = document.querySelector("title");
505
+ if (title != null)
506
+ options.defaultTitle = title.textContent ?? void 0;
507
+ const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
508
+ if (defaultIconContent) {
509
+ try {
510
+ options.defaultIcon = JSON5.parse(defaultIconContent);
511
+ } catch (err) {
512
+ config.logger.fatal(
513
+ `Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
514
+ err
515
+ );
516
+ }
517
+ }
518
+ const mv2KeyContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
519
+ if (mv2KeyContent) {
520
+ options.mv2Key = mv2KeyContent === "page_action" ? "page_action" : "browser_action";
521
+ }
522
+ return {
523
+ type: "popup",
524
+ name: "popup",
525
+ options,
526
+ inputPath: path5,
527
+ outputDir: config.outDir
528
+ };
529
+ }
530
+ async function getOptionsEntrypoint(config, path5) {
531
+ const options = {};
532
+ const content = await fs4.readFile(path5, "utf-8");
533
+ const { document } = parseHTML2(content);
534
+ const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
535
+ if (openInTabContent) {
536
+ options.openInTab = Boolean(openInTabContent);
537
+ }
538
+ const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
539
+ if (chromeStyleContent) {
540
+ options.chromeStyle = Boolean(chromeStyleContent);
541
+ }
542
+ const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
543
+ if (browserStyleContent) {
544
+ options.browserStyle = Boolean(browserStyleContent);
545
+ }
546
+ return {
547
+ type: "options",
548
+ name: "options",
549
+ options,
550
+ inputPath: path5,
551
+ outputDir: config.outDir
552
+ };
553
+ }
554
+ async function getBackgroundEntrypoint(config, path5) {
555
+ const { main: _, ...options } = await importTsFile(config.root, path5);
556
+ if (options == null) {
557
+ throw Error("Background script does not have a default export");
558
+ }
559
+ return {
560
+ type: "background",
561
+ name: "background",
562
+ inputPath: path5,
563
+ outputDir: config.outDir,
564
+ options
565
+ };
566
+ }
567
+ async function getContentScriptEntrypoint(config, name, path5) {
568
+ const { main: _, ...options } = await importTsFile(
569
+ config.root,
570
+ path5
571
+ );
572
+ if (options == null) {
573
+ throw Error(`Content script ${name} does not have a default export`);
574
+ }
575
+ return {
576
+ type: "content-script",
577
+ name: getEntrypointName(config.entrypointsDir, path5),
578
+ inputPath: path5,
579
+ outputDir: resolve8(config.outDir, "content-scripts"),
580
+ options
581
+ };
582
+ }
583
+ var PATH_GLOB_TO_TYPE_MAP = {
584
+ "sandbox.html": "sandbox",
585
+ "sandbox/index.html": "sandbox",
586
+ "*.sandbox.html": "sandbox",
587
+ "*.sandbox/index.html": "sandbox",
588
+ "bookmarks.html": "bookmarks",
589
+ "bookmarks/index.html": "bookmarks",
590
+ "history.html": "history",
591
+ "history/index.html": "history",
592
+ "newtab.html": "newtab",
593
+ "newtab/index.html": "newtab",
594
+ "sidepanel.html": "sidepanel",
595
+ "sidepanel/index.html": "sidepanel",
596
+ "*.sidepanel.html": "sidepanel",
597
+ "*.sidepanel/index.html": "sidepanel",
598
+ "devtools.html": "devtools",
599
+ "devtools/index.html": "devtools",
600
+ "background.ts": "background",
601
+ "*.content.ts?(x)": "content-script",
602
+ "*.content/index.ts?(x)": "content-script",
603
+ "popup.html": "popup",
604
+ "popup/index.html": "popup",
605
+ "options.html": "options",
606
+ "options/index.html": "options",
607
+ "*.html": "unlisted-page",
608
+ "*/index.html": "unlisted-page",
609
+ "*.ts": "unlisted-script",
610
+ // Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files
611
+ "*/*": "ignored"
612
+ };
613
+
614
+ // src/core/build/buildEntrypoints.ts
615
+ import * as vite2 from "vite";
616
+
617
+ // src/core/utils/groupEntrypoints.ts
618
+ function groupEntrypoints(entrypoints) {
619
+ const groupIndexMap = {};
620
+ const groups = [];
621
+ for (const entry of entrypoints) {
622
+ const group = ENTRY_TYPE_TO_GROUP_MAP[entry.type];
623
+ if (group === "no-group") {
624
+ groups.push(entry);
625
+ } else {
626
+ let groupIndex = groupIndexMap[group];
627
+ if (groupIndex == null) {
628
+ groupIndex = groups.push([]) - 1;
629
+ groupIndexMap[group] = groupIndex;
630
+ }
631
+ groups[groupIndex].push(entry);
632
+ }
633
+ }
634
+ return groups;
635
+ }
636
+ var ENTRY_TYPE_TO_GROUP_MAP = {
637
+ sandbox: "sandbox-page",
638
+ popup: "extension-page",
639
+ newtab: "extension-page",
640
+ history: "extension-page",
641
+ options: "extension-page",
642
+ devtools: "extension-page",
643
+ bookmarks: "extension-page",
644
+ sidepanel: "extension-page",
645
+ "unlisted-page": "extension-page",
646
+ background: "no-group",
647
+ "content-script": "no-group",
648
+ "unlisted-script": "no-group"
649
+ };
650
+
651
+ // src/core/utils/removeEmptyDirs.ts
652
+ import fs5 from "fs-extra";
653
+ import path3 from "path";
654
+ async function removeEmptyDirs(dir) {
655
+ const files = await fs5.readdir(dir);
656
+ for (const file of files) {
657
+ const filePath = path3.join(dir, file);
658
+ const stats = await fs5.stat(filePath);
659
+ if (stats.isDirectory()) {
660
+ await removeEmptyDirs(filePath);
661
+ }
662
+ }
663
+ try {
664
+ await fs5.rmdir(dir);
665
+ } catch {
666
+ }
667
+ }
668
+
669
+ // src/core/build/buildEntrypoints.ts
670
+ import glob2 from "fast-glob";
671
+ import fs6 from "fs-extra";
672
+ import { dirname as dirname4, resolve as resolve9 } from "path";
673
+ async function buildEntrypoints(entrypoints, config) {
674
+ const groups = groupEntrypoints(entrypoints);
675
+ const outputs = [];
676
+ for (const group of groups) {
677
+ const output = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config);
678
+ outputs.push(output);
679
+ }
680
+ const publicOutput = await copyPublicDirectory(config);
681
+ outputs.push(publicOutput);
682
+ await removeEmptyDirs(config.outDir);
683
+ return outputs.flat();
684
+ }
685
+ async function buildSingleEntrypoint(entrypoint, config) {
686
+ const isVirtual = ["background", "content-script"].includes(entrypoint.type);
687
+ const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
688
+ const libMode = {
689
+ build: {
690
+ lib: {
691
+ entry,
692
+ formats: ["iife"],
693
+ name: entrypoint.name,
694
+ fileName: entrypoint.name
695
+ },
696
+ rollupOptions: {
697
+ output: {
698
+ entryFileNames: getEntrypointBundlePath(
699
+ entrypoint,
700
+ config.outDir,
701
+ ".js"
702
+ ),
703
+ // Output content script CSS to assets/ with a hash to prevent conflicts. Defaults to
704
+ // "[name].[ext]" in lib mode, which usually results in "style.css". That means multiple
705
+ // content scripts with styles would overwrite each other if it weren't changed below.
706
+ assetFileNames: `assets/${entrypoint.name}-[hash].[ext]`
707
+ }
708
+ }
709
+ }
710
+ };
711
+ const entryConfig = vite2.mergeConfig(
712
+ libMode,
713
+ config.vite
714
+ );
715
+ const result = await vite2.build(entryConfig);
716
+ return getBuildOutput(result);
717
+ }
718
+ async function buildMultipleEntrypoints(entrypoints, config) {
719
+ const multiPage = {
720
+ plugins: [multipageMove(entrypoints, config)],
721
+ build: {
722
+ rollupOptions: {
723
+ input: entrypoints.reduce((input, entry) => {
724
+ input[entry.name] = entry.inputPath;
725
+ return input;
726
+ }, {})
727
+ }
728
+ }
729
+ };
730
+ const entryConfig = vite2.mergeConfig(
731
+ multiPage,
732
+ config.vite
733
+ );
734
+ const result = await vite2.build(entryConfig);
735
+ return getBuildOutput(result);
736
+ }
737
+ function getBuildOutput(result) {
738
+ if ("on" in result)
739
+ throw Error("wxt does not support vite watch mode.");
740
+ if (Array.isArray(result))
741
+ return result.flatMap(({ output }) => output);
742
+ return result.output;
743
+ }
744
+ async function copyPublicDirectory(config) {
745
+ if (!await fs6.exists(config.publicDir))
746
+ return [];
747
+ const files = await glob2("**/*", { cwd: config.publicDir });
748
+ const outputs = [];
749
+ for (const file of files) {
750
+ const srcPath = resolve9(config.publicDir, file);
751
+ const outPath = resolve9(config.outDir, file);
752
+ await fs6.ensureDir(dirname4(outPath));
753
+ await fs6.copyFile(srcPath, outPath);
754
+ outputs.push({
755
+ type: "asset",
756
+ fileName: file,
757
+ name: file,
758
+ needsCodeReference: false,
759
+ source: await fs6.readFile(srcPath)
760
+ });
761
+ }
762
+ return outputs;
763
+ }
764
+
765
+ // src/core/utils/manifest.ts
766
+ import fs7 from "fs-extra";
767
+ import { resolve as resolve10 } from "path";
768
+
769
+ // src/core/utils/ContentSecurityPolicy.ts
770
+ var ContentSecurityPolicy = class _ContentSecurityPolicy {
771
+ static DIRECTIVE_ORDER = {
772
+ "default-src": 0,
773
+ "script-src": 1,
774
+ "object-src": 2
775
+ };
776
+ data;
777
+ constructor(csp) {
778
+ if (csp) {
779
+ const sections = csp.split(";").map((section) => section.trim());
780
+ this.data = sections.reduce((data, section) => {
781
+ const [key, ...values] = section.split(" ").map((item) => item.trim());
782
+ if (key)
783
+ data[key] = values;
784
+ return data;
785
+ }, {});
786
+ } else {
787
+ this.data = {};
788
+ }
789
+ }
790
+ /**
791
+ * Ensure a set of values are listed under a directive.
792
+ */
793
+ add(directive, ...newValues) {
794
+ const values = this.data[directive] ?? [];
795
+ newValues.forEach((newValue) => {
796
+ if (!values.includes(newValue))
797
+ values.push(newValue);
798
+ });
799
+ this.data[directive] = values;
800
+ return this;
801
+ }
802
+ toString() {
803
+ const directives = Object.entries(this.data).sort(([l], [r]) => {
804
+ const lo = _ContentSecurityPolicy.DIRECTIVE_ORDER[l] ?? 2;
805
+ const ro = _ContentSecurityPolicy.DIRECTIVE_ORDER[r] ?? 2;
806
+ return lo - ro;
807
+ });
808
+ return directives.map((entry) => entry.flat().join(" ")).join("; ") + ";";
809
+ }
810
+ };
811
+
812
+ // src/core/utils/manifest.ts
813
+ async function writeManifest(manifest, output, config) {
814
+ const str = config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
815
+ await fs7.ensureDir(config.outDir);
816
+ await fs7.writeFile(resolve10(config.outDir, "manifest.json"), str, "utf-8");
817
+ output.unshift({
818
+ type: "asset",
819
+ fileName: "manifest.json",
820
+ name: "manifest",
821
+ needsCodeReference: false,
822
+ source: str
823
+ });
824
+ }
825
+ async function generateMainfest(entrypoints, buildOutput, config) {
826
+ const pkg = await getPackageJson(config);
827
+ if (pkg.version == null)
828
+ throw Error("package.json does not include a version");
829
+ if (pkg.name == null)
830
+ throw Error("package.json does not include a name");
831
+ if (pkg.description == null)
832
+ throw Error("package.json does not include a description");
833
+ const manifest = {
834
+ manifest_version: config.manifestVersion,
835
+ name: pkg.name,
836
+ short_name: pkg.shortName,
837
+ version: simplifyVersion(pkg.version),
838
+ version_name: config.browser === "firefox" ? void 0 : pkg.version,
839
+ ...config.manifest
840
+ };
841
+ addEntrypoints(manifest, entrypoints, buildOutput, config);
842
+ if (config.command === "serve")
843
+ addDevModeCsp(manifest, config);
844
+ return manifest;
845
+ }
846
+ async function getPackageJson(config) {
847
+ return await fs7.readJson(resolve10(config.root, "package.json"));
848
+ }
849
+ function simplifyVersion(versionName) {
850
+ const version3 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
851
+ versionName
852
+ )?.[1];
853
+ if (version3 == null)
854
+ throw Error(
855
+ `Cannot simplify package.json version "${versionName}" to a valid extension version, "X.Y.Z"`
856
+ );
857
+ return version3;
858
+ }
859
+ function addEntrypoints(manifest, entrypoints, buildOutput, config) {
860
+ const entriesByType = entrypoints.reduce((map, entrypoint) => {
861
+ map[entrypoint.type] ??= [];
862
+ map[entrypoint.type]?.push(entrypoint);
863
+ return map;
864
+ }, {});
865
+ const background = entriesByType["background"]?.[0];
866
+ const bookmarks = entriesByType["bookmarks"]?.[0];
867
+ const contentScripts = entriesByType["content-script"];
868
+ const devtools = entriesByType["devtools"]?.[0];
869
+ const history = entriesByType["history"]?.[0];
870
+ const newtab = entriesByType["newtab"]?.[0];
871
+ const options = entriesByType["options"]?.[0];
872
+ const popup = entriesByType["popup"]?.[0];
873
+ const sandboxes = entriesByType["sandbox"];
874
+ const sidepanels = entriesByType["sidepanel"];
875
+ if (background) {
876
+ const script = getEntrypointBundlePath(background, config.outDir, ".js");
877
+ if (manifest.manifest_version === 3) {
878
+ manifest.background = {
879
+ type: background.options.type,
880
+ service_worker: script
881
+ };
882
+ } else {
883
+ manifest.background = {
884
+ persistent: background.options.persistent,
885
+ scripts: [script]
886
+ };
887
+ }
888
+ }
889
+ if (bookmarks) {
890
+ if (config.browser === "firefox") {
891
+ config.logger.warn(
892
+ "Bookmarks are not supported by Firefox. chrome_url_overrides.bookmarks was not added to the manifest"
893
+ );
894
+ } else {
895
+ manifest.chrome_url_overrides ??= {};
896
+ manifest.chrome_url_overrides.bookmarks = getEntrypointBundlePath(
897
+ bookmarks,
898
+ config.outDir,
899
+ ".html"
900
+ );
901
+ }
902
+ }
903
+ if (history) {
904
+ if (config.browser === "firefox") {
905
+ config.logger.warn(
906
+ "Bookmarks are not supported by Firefox. chrome_url_overrides.history was not added to the manifest"
907
+ );
908
+ } else {
909
+ manifest.chrome_url_overrides ??= {};
910
+ manifest.chrome_url_overrides.history = getEntrypointBundlePath(
911
+ history,
912
+ config.outDir,
913
+ ".html"
914
+ );
915
+ }
916
+ }
917
+ if (newtab) {
918
+ manifest.chrome_url_overrides ??= {};
919
+ manifest.chrome_url_overrides.newtab = getEntrypointBundlePath(
920
+ newtab,
921
+ config.outDir,
922
+ ".html"
923
+ );
924
+ }
925
+ if (popup) {
926
+ const default_popup = getEntrypointBundlePath(
927
+ popup,
928
+ config.outDir,
929
+ ".html"
930
+ );
931
+ const options2 = {
932
+ default_icon: popup.options.defaultIcon,
933
+ default_title: popup.options.defaultTitle
934
+ };
935
+ if (manifest.manifest_version === 3) {
936
+ manifest.action = {
937
+ ...options2,
938
+ default_popup
939
+ };
940
+ } else {
941
+ manifest[popup.options.mv2Key ?? "browser_action"] = {
942
+ ...options2,
943
+ default_popup
944
+ };
945
+ }
946
+ }
947
+ if (devtools) {
948
+ manifest.devtools_page = getEntrypointBundlePath(
949
+ devtools,
950
+ config.outDir,
951
+ ".html"
952
+ );
953
+ }
954
+ if (options) {
955
+ const page = getEntrypointBundlePath(options, config.outDir, ".html");
956
+ manifest.options_ui = {
957
+ open_in_tab: options.options.openInTab,
958
+ browser_style: config.browser === "firefox" ? options.options.browserStyle : void 0,
959
+ chrome_style: config.browser !== "firefox" ? options.options.chromeStyle : void 0,
960
+ page
961
+ };
962
+ }
963
+ if (sandboxes?.length) {
964
+ if (config.browser === "firefox") {
965
+ config.logger.warn(
966
+ "Sandboxed pages not supported by Firefox. sandbox.pages was not added to the manifest"
967
+ );
968
+ } else {
969
+ manifest.sandbox = {
970
+ pages: sandboxes.map(
971
+ (entry) => getEntrypointBundlePath(entry, config.outDir, ".html")
972
+ )
973
+ };
974
+ }
975
+ }
976
+ if (sidepanels?.length) {
977
+ const defaultSidepanel = sidepanels.find((entry) => entry.name === "sidepanel") ?? sidepanels[0];
978
+ const page = getEntrypointBundlePath(
979
+ defaultSidepanel,
980
+ config.outDir,
981
+ ".html"
982
+ );
983
+ if (config.browser === "firefox") {
984
+ manifest.sidebar_action = {
985
+ // TODO: Add options to side panel
986
+ // ...defaultSidepanel.options,
987
+ default_panel: page
988
+ };
989
+ } else if (config.manifestVersion === 3) {
990
+ manifest.side_panel = {
991
+ default_path: page
992
+ };
993
+ } else {
994
+ config.logger.warn(
995
+ "Side panel not supported by Chromium using MV2. side_panel.default_path was not added to the manifest"
996
+ );
997
+ }
998
+ }
999
+ if (contentScripts?.length) {
1000
+ if (config.command === "serve") {
1001
+ const permissionsKey = config.manifestVersion === 2 ? "permissions" : "host_permissions";
1002
+ const hostPermissions = new Set(manifest[permissionsKey] ?? []);
1003
+ contentScripts.forEach((script) => {
1004
+ script.options.matches.forEach((matchPattern) => {
1005
+ hostPermissions.add(matchPattern);
1006
+ });
1007
+ });
1008
+ manifest[permissionsKey] = Array.from(hostPermissions).sort();
1009
+ } else {
1010
+ const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
1011
+ const hash = JSON.stringify(script.options);
1012
+ if (!map.has(hash)) {
1013
+ map.set(hash, [script]);
1014
+ } else {
1015
+ map.get(hash)?.push(script);
1016
+ }
1017
+ return map;
1018
+ }, /* @__PURE__ */ new Map());
1019
+ manifest.content_scripts = Array.from(hashToEntrypointsMap.entries()).map(
1020
+ ([, scripts]) => ({
1021
+ ...scripts[0].options,
1022
+ // TOOD: Sorting css and js arrays here so we get consistent test results... but we
1023
+ // shouldn't have to. Where is the inconsistency coming from?
1024
+ css: getContentScriptCssFiles(scripts, buildOutput)?.sort(),
1025
+ js: scripts.map(
1026
+ (entry) => getEntrypointBundlePath(entry, config.outDir, ".js")
1027
+ ).sort()
1028
+ })
1029
+ );
1030
+ }
1031
+ }
1032
+ }
1033
+ function addDevModeCsp(manifest, config) {
1034
+ const permission = `http://${config.server?.hostname ?? ""}/*`;
1035
+ const allowedCsp = config.server?.origin ?? "http://localhost:*";
1036
+ if (manifest.manifest_version === 3) {
1037
+ manifest.host_permissions ??= [];
1038
+ if (!manifest.host_permissions.includes(permission))
1039
+ manifest.host_permissions.push(permission);
1040
+ } else {
1041
+ manifest.permissions ??= [];
1042
+ if (!manifest.permissions.includes(permission))
1043
+ manifest.permissions.push(permission);
1044
+ }
1045
+ const csp = new ContentSecurityPolicy(
1046
+ manifest.manifest_version === 3 ? (
1047
+ // @ts-expect-error: extension_pages is not typed
1048
+ manifest.content_security_policy?.extension_pages ?? "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
1049
+ ) : manifest.content_security_policy ?? "script-src 'self'; object-src 'self';"
1050
+ // default CSP for MV2
1051
+ );
1052
+ if (config.server)
1053
+ csp.add("script-src", allowedCsp);
1054
+ if (manifest.manifest_version === 3) {
1055
+ manifest.content_security_policy ??= {};
1056
+ manifest.content_security_policy.extension_pages = csp.toString();
1057
+ } else {
1058
+ manifest.content_security_policy = csp.toString();
1059
+ }
1060
+ }
1061
+ function getContentScriptCssFiles(contentScripts, buildOutput) {
1062
+ const css = [];
1063
+ contentScripts.forEach((script) => {
1064
+ const cssRegex = new RegExp(`^assets/${script.name}-[a-f0-9]{8}.css$`);
1065
+ const relatedCss = buildOutput.find(
1066
+ (chunk) => chunk.fileName.match(cssRegex)
1067
+ );
1068
+ if (relatedCss)
1069
+ css.push(relatedCss.fileName);
1070
+ });
1071
+ if (css.length > 0)
1072
+ return css;
1073
+ return void 0;
1074
+ }
1075
+
1076
+ // src/core/log/printBuildSummary.ts
1077
+ import path4, { extname as extname2, relative as relative4, resolve as resolve11 } from "path";
1078
+
1079
+ // src/core/log/printTable.ts
1080
+ function printTable(log, rows, gap = 2) {
1081
+ if (rows.length === 0)
1082
+ return;
1083
+ const columnWidths = rows.reduce(
1084
+ (widths, row) => {
1085
+ for (let i = 0; i < Math.max(widths.length, row.length); i++) {
1086
+ widths[i] = Math.max(row[i]?.length ?? 0, widths[i] ?? 0);
1087
+ }
1088
+ return widths;
1089
+ },
1090
+ rows[0].map((column) => column.length)
1091
+ );
1092
+ let str = "";
1093
+ rows.forEach((row, i) => {
1094
+ row.forEach((col, j) => {
1095
+ str += col.padEnd(columnWidths[j], " ");
1096
+ if (j !== row.length - 1)
1097
+ str += "".padEnd(gap, " ");
1098
+ });
1099
+ if (i !== rows.length - 1)
1100
+ str += "\n";
1101
+ });
1102
+ log(str);
1103
+ }
1104
+
1105
+ // src/core/log/printBuildSummary.ts
1106
+ import pc from "picocolors";
1107
+ import fs8 from "fs-extra";
1108
+ import { filesize } from "filesize";
1109
+ async function printBuildSummary(output, config) {
1110
+ const chunks = output.sort((l, r) => {
1111
+ const lWeight = CHUNK_SORT_WEIGHTS[l.fileName] ?? CHUNK_SORT_WEIGHTS[extname2(l.fileName)] ?? DEFAULT_SORT_WEIGHT;
1112
+ const rWeight = CHUNK_SORT_WEIGHTS[r.fileName] ?? CHUNK_SORT_WEIGHTS[extname2(r.fileName)] ?? DEFAULT_SORT_WEIGHT;
1113
+ const diff = lWeight - rWeight;
1114
+ if (diff !== 0)
1115
+ return diff;
1116
+ return l.fileName.localeCompare(r.fileName);
1117
+ });
1118
+ let totalSize = 0;
1119
+ const chunkRows = await Promise.all(
1120
+ chunks.map(async (chunk, i) => {
1121
+ const file = [
1122
+ relative4(process.cwd(), config.outDir) + path4.sep,
1123
+ chunk.fileName
1124
+ ];
1125
+ const ext = extname2(chunk.fileName);
1126
+ const prefix = i === chunks.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
1127
+ const color = CHUNK_COLORS[ext] ?? DEFAULT_COLOR;
1128
+ const stats = await fs8.lstat(resolve11(config.outDir, chunk.fileName));
1129
+ totalSize += stats.size;
1130
+ const size = String(filesize(stats.size));
1131
+ return [
1132
+ `${pc.gray(prefix)} ${pc.dim(file[0])}${color(file[1])}`,
1133
+ pc.dim(size)
1134
+ ];
1135
+ })
1136
+ );
1137
+ printTable(config.logger.log, chunkRows);
1138
+ config.logger.log(
1139
+ `${pc.cyan("\u03A3 Total size:")} ${String(filesize(totalSize))}`
1140
+ );
1141
+ }
1142
+ var DEFAULT_SORT_WEIGHT = 100;
1143
+ var CHUNK_SORT_WEIGHTS = {
1144
+ "manifest.json": 0,
1145
+ ".html": 1,
1146
+ ".js": 2,
1147
+ ".css": 3
1148
+ };
1149
+ var DEFAULT_COLOR = pc.blue;
1150
+ var CHUNK_COLORS = {
1151
+ ".html": pc.green,
1152
+ ".css": pc.magenta,
1153
+ ".js": pc.cyan
1154
+ };
1155
+
1156
+ // src/index.ts
1157
+ import fs10 from "fs-extra";
1158
+
1159
+ // src/core/build/generateTypesDir.ts
1160
+ import { createUnimport as createUnimport2 } from "unimport";
1161
+ import fs9 from "fs-extra";
1162
+ import { relative as relative5, resolve as resolve12 } from "path";
1163
+ async function generateTypesDir(entrypoints, config) {
1164
+ await fs9.ensureDir(config.typesDir);
1165
+ const references = [];
1166
+ references.push(await writeImportsDeclarationFile(config));
1167
+ references.push(await writePathsDeclarationFile(entrypoints, config));
1168
+ references.push(await writeGlobalsDeclarationFile(config));
1169
+ const mainReference = await writeMainDeclarationFile(references, config);
1170
+ await writeTsConfigFile(mainReference, config);
1171
+ }
1172
+ async function writeImportsDeclarationFile(config) {
1173
+ const filePath = resolve12(config.typesDir, "imports.d.ts");
1174
+ const unimport2 = createUnimport2(getUnimportOptions(config));
1175
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
1176
+ await fs9.writeFile(
1177
+ filePath,
1178
+ ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
1179
+ "\n"
1180
+ ) + "\n"
1181
+ );
1182
+ return filePath;
1183
+ }
1184
+ async function writePathsDeclarationFile(entrypoints, config) {
1185
+ const filePath = resolve12(config.typesDir, "paths.d.ts");
1186
+ await fs9.writeFile(
1187
+ filePath,
1188
+ [
1189
+ "// Generated by wxt",
1190
+ "type EntrypointPath =",
1191
+ ...entrypoints.map((entry) => {
1192
+ const path5 = getEntrypointBundlePath(
1193
+ entry,
1194
+ config.outDir,
1195
+ entry.inputPath.endsWith(".html") ? ".html" : ".js"
1196
+ );
1197
+ return ` | "/${path5}"`;
1198
+ }).sort()
1199
+ ].join("\n") + "\n"
1200
+ );
1201
+ return filePath;
1202
+ }
1203
+ async function writeGlobalsDeclarationFile(config) {
1204
+ const filePath = resolve12(config.typesDir, "globals.d.ts");
1205
+ const globals = getGlobals(config);
1206
+ await fs9.writeFile(
1207
+ filePath,
1208
+ [
1209
+ "// Generated by wxt",
1210
+ "export {}",
1211
+ "declare global {",
1212
+ ...globals.map((global) => ` const ${global.name}: ${global.type};`),
1213
+ "}"
1214
+ ].join("\n") + "\n",
1215
+ "utf-8"
1216
+ );
1217
+ return filePath;
1218
+ }
1219
+ async function writeMainDeclarationFile(references, config) {
1220
+ const dir = config.wxtDir;
1221
+ const filePath = resolve12(dir, "wxt.d.ts");
1222
+ await fs9.writeFile(
1223
+ filePath,
1224
+ [
1225
+ "// Generated by wxt",
1226
+ ...references.map(
1227
+ (ref) => `/// <reference types="./${relative5(dir, ref)}" />`
1228
+ )
1229
+ ].join("\n") + "\n"
1230
+ );
1231
+ return filePath;
1232
+ }
1233
+ async function writeTsConfigFile(mainReference, config) {
1234
+ const dir = config.wxtDir;
1235
+ await fs9.writeFile(
1236
+ resolve12(dir, "tsconfig.json"),
1237
+ `{
1238
+ "compilerOptions": {
1239
+ "target": "ESNext",
1240
+ "module": "ESNext",
1241
+ "moduleResolution": "Bundler",
1242
+ "noEmit": true,
1243
+ "esModuleInterop": true,
1244
+ "forceConsistentCasingInFileNames": true,
1245
+ "resolveJsonModule": true,
1246
+
1247
+ /* Type Checking */
1248
+ "strict": true,
1249
+
1250
+ /* Completeness */
1251
+ "skipLibCheck": true
1252
+ },
1253
+ "include": [
1254
+ "${relative5(dir, config.root)}/**/*",
1255
+ "./${relative5(dir, mainReference)}"
1256
+ ],
1257
+ "exclude": ["${relative5(dir, config.outBaseDir)}"]
1258
+ }`
1259
+ );
1260
+ }
1261
+
1262
+ // src/index.ts
1263
+ import pc2 from "picocolors";
1264
+ import * as vite3 from "vite";
1265
+
1266
+ // src/core/utils/findOpenPort.ts
1267
+ import net from "node:net";
1268
+ function findOpenPort(startPort, endPort) {
1269
+ return findOpenPortRecursive(startPort, startPort, endPort);
1270
+ }
1271
+ function findOpenPortRecursive(port, startPort, endPort) {
1272
+ return new Promise((resolve13, reject) => {
1273
+ if (port > endPort)
1274
+ return reject(
1275
+ Error(`Could not find open port between ${startPort}-${endPort}`)
1276
+ );
1277
+ const server = net.createServer();
1278
+ server.listen(port, () => {
1279
+ server.once("close", () => resolve13(port));
1280
+ server.close();
1281
+ });
1282
+ server.on(
1283
+ "error",
1284
+ () => resolve13(findOpenPortRecursive(port + 1, startPort, endPort))
1285
+ );
1286
+ });
1287
+ }
1288
+
1289
+ // src/core/utils/formatDuration.ts
1290
+ function formatDuration(duration) {
1291
+ if (duration < 1e3)
1292
+ return `${duration} ms`;
1293
+ if (duration < 1e4)
1294
+ return `${(duration / 1e3).toFixed(3)} s`;
1295
+ if (duration < 6e4)
1296
+ return `${(duration / 1e3).toFixed(1)} s`;
1297
+ return `${(duration / 1e3).toFixed(0)} s`;
1298
+ }
1299
+
1300
+ // src/core/runners/createWebExtRunner.ts
1301
+ function createWebExtRunner() {
1302
+ let runner;
1303
+ return {
1304
+ async openBrowser(config) {
1305
+ if (config.browser === "safari") {
1306
+ config.logger.warn("Cannot open safari automatically.");
1307
+ return;
1308
+ }
1309
+ const webExtLogger = await import("web-ext/util/logger");
1310
+ webExtLogger.consoleStream.write = ({ level, msg, name }) => {
1311
+ if (level >= ERROR_LOG_LEVEL)
1312
+ config.logger.error(name, msg);
1313
+ if (level >= WARN_LOG_LEVEL)
1314
+ config.logger.warn(msg);
1315
+ };
1316
+ const wxtUserConfig = config.runnerConfig.config;
1317
+ const userConfig = {
1318
+ console: wxtUserConfig?.openConsole,
1319
+ devtools: wxtUserConfig?.openDevtools,
1320
+ startUrl: wxtUserConfig?.startUrls,
1321
+ ...config.browser === "firefox" ? {
1322
+ firefox: wxtUserConfig?.binaries?.firefox,
1323
+ firefoxProfile: wxtUserConfig?.firefoxProfile,
1324
+ prefs: wxtUserConfig?.firefoxPrefs,
1325
+ args: wxtUserConfig?.firefoxArgs
1326
+ } : {
1327
+ chromiumBinary: wxtUserConfig?.binaries?.[config.browser],
1328
+ chromiumProfile: wxtUserConfig?.chromiumProfile,
1329
+ args: wxtUserConfig?.chromiumArgs
1330
+ }
1331
+ };
1332
+ const finalConfig = {
1333
+ ...userConfig,
1334
+ target: config.browser === "firefox" ? "firefox-desktop" : "chromium",
1335
+ sourceDir: config.outDir,
1336
+ // WXT handles reloads, so disable auto-reload behaviors in web-ext
1337
+ noReload: true,
1338
+ noInput: true
1339
+ };
1340
+ const options = {
1341
+ // Don't call `process.exit(0)` after starting web-ext
1342
+ shouldExitProgram: false
1343
+ };
1344
+ config.logger.debug("web-ext config:", finalConfig);
1345
+ config.logger.debug("web-ext options:", options);
1346
+ const webExt = await import("web-ext");
1347
+ runner = await webExt.default.cmd.run(finalConfig, options);
1348
+ },
1349
+ async closeBrowser() {
1350
+ return await runner?.exit();
1351
+ }
1352
+ };
1353
+ }
1354
+ var WARN_LOG_LEVEL = 40;
1355
+ var ERROR_LOG_LEVEL = 50;
1356
+
1357
+ // package.json
1358
+ var version = "0.0.1";
1359
+
1360
+ // src/core/utils/defineConfig.ts
1361
+ function defineConfig(config) {
1362
+ return config;
1363
+ }
1364
+
1365
+ // src/core/utils/defineRunnerConfig.ts
1366
+ function defineRunnerConfig(config) {
1367
+ return config;
1368
+ }
1369
+
1370
+ // src/index.ts
1371
+ async function build2(config) {
1372
+ const internalConfig = await getInternalConfig(config, "build");
1373
+ return await buildInternal(internalConfig);
1374
+ }
1375
+ async function createServer2(config) {
1376
+ const port = await findOpenPort(3e3, 3010);
1377
+ const hostname = "localhost";
1378
+ const origin = `http://${hostname}:${port}`;
1379
+ const serverConfig = {
1380
+ server: {
1381
+ origin
1382
+ }
1383
+ };
1384
+ const internalConfig = await getInternalConfig(
1385
+ vite3.mergeConfig(serverConfig, config ?? {}),
1386
+ "serve"
1387
+ );
1388
+ const runner = createWebExtRunner();
1389
+ const viteServer = await vite3.createServer(internalConfig.vite);
1390
+ const server = {
1391
+ ...viteServer,
1392
+ async listen(port2, isRestart) {
1393
+ const res = await viteServer.listen(port2, isRestart);
1394
+ if (!isRestart) {
1395
+ internalConfig.logger.success(`Started dev server @ ${origin}`);
1396
+ internalConfig.logger.info("Opening browser...");
1397
+ await runner.openBrowser(internalConfig);
1398
+ internalConfig.logger.success("Opened!");
1399
+ }
1400
+ return res;
1401
+ },
1402
+ logger: internalConfig.logger,
1403
+ port,
1404
+ hostname,
1405
+ origin
1406
+ };
1407
+ internalConfig.logger.info("Created dev server");
1408
+ internalConfig.server = server;
1409
+ await buildInternal(internalConfig);
1410
+ return server;
1411
+ }
1412
+ async function buildInternal(config) {
1413
+ const verb = config.command === "serve" ? "Pre-rendering" : "Building";
1414
+ const target = `${config.browser}-mv${config.manifestVersion}`;
1415
+ config.logger.info(
1416
+ `${verb} ${pc2.cyan(target)} for ${pc2.cyan(config.mode)} with ${pc2.green(
1417
+ `Vite ${vite3.version}`
1418
+ )}`
1419
+ );
1420
+ const startTime = Date.now();
1421
+ await fs10.rm(config.outDir, { recursive: true, force: true });
1422
+ await fs10.ensureDir(config.outDir);
1423
+ const entrypoints = await findEntrypoints(config);
1424
+ await generateTypesDir(entrypoints, config);
1425
+ const output = await buildEntrypoints(entrypoints, config);
1426
+ const manifest = await generateMainfest(entrypoints, output, config);
1427
+ await writeManifest(manifest, output, config);
1428
+ config.logger.success(
1429
+ `Built extension in ${formatDuration(Date.now() - startTime)}`
1430
+ );
1431
+ await printBuildSummary(output, config);
1432
+ return output;
1433
+ }
1434
+ export {
1435
+ build2 as build,
1436
+ createServer2 as createServer,
1437
+ defineConfig,
1438
+ defineRunnerConfig,
1439
+ version
1440
+ };
1441
+ //# sourceMappingURL=index.js.map