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