wxt 0.10.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,112 @@
1
1
  // package.json
2
- var version = "0.10.4";
2
+ var version = "0.11.0";
3
3
 
4
- // src/core/utils/entrypoints.ts
5
- import path, { relative, resolve } from "node:path";
4
+ // src/core/utils/arrays.ts
5
+ function every(array, predicate) {
6
+ for (let i = 0; i < array.length; i++)
7
+ if (!predicate(array[i], i))
8
+ return false;
9
+ return true;
10
+ }
6
11
 
7
12
  // src/core/utils/paths.ts
8
13
  import systemPath from "node:path";
9
14
  import normalize from "normalize-path";
10
- function normalizePath(path7) {
11
- return normalize(path7);
15
+ function normalizePath(path6) {
16
+ return normalize(path6);
12
17
  }
13
- function unnormalizePath(path7) {
14
- return systemPath.normalize(path7);
18
+ function unnormalizePath(path6) {
19
+ return systemPath.normalize(path6);
15
20
  }
16
21
  var CSS_EXTENSIONS = ["css", "scss", "sass", "less", "styl", "stylus"];
17
22
  var CSS_EXTENSIONS_PATTERN = `+(${CSS_EXTENSIONS.join("|")})`;
18
23
 
24
+ // src/core/utils/building/detect-dev-changes.ts
25
+ function detectDevChanges(changedFiles, currentOutput) {
26
+ if (currentOutput == null)
27
+ return { type: "no-change" };
28
+ const changedSteps = new Set(
29
+ changedFiles.flatMap(
30
+ (changedFile) => findEffectedSteps(changedFile, currentOutput)
31
+ )
32
+ );
33
+ if (changedSteps.size === 0)
34
+ return { type: "no-change" };
35
+ const unchangedOutput = {
36
+ manifest: currentOutput.manifest,
37
+ steps: [],
38
+ publicAssets: []
39
+ };
40
+ const changedOutput = {
41
+ manifest: currentOutput.manifest,
42
+ steps: [],
43
+ publicAssets: []
44
+ };
45
+ for (const step of currentOutput.steps) {
46
+ if (changedSteps.has(step)) {
47
+ changedOutput.steps.push(step);
48
+ } else {
49
+ unchangedOutput.steps.push(step);
50
+ }
51
+ }
52
+ for (const asset of currentOutput.publicAssets) {
53
+ if (changedSteps.has(asset)) {
54
+ changedOutput.publicAssets.push(asset);
55
+ } else {
56
+ unchangedOutput.publicAssets.push(asset);
57
+ }
58
+ }
59
+ const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, ([_, file]) => file.endsWith(".html"));
60
+ if (isOnlyHtmlChanges) {
61
+ return {
62
+ type: "html-reload",
63
+ cachedOutput: unchangedOutput,
64
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
65
+ };
66
+ }
67
+ const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
68
+ changedOutput.steps.flatMap((step) => step.entrypoints),
69
+ (entry) => entry.type === "content-script"
70
+ );
71
+ if (isOnlyContentScripts) {
72
+ return {
73
+ type: "content-script-reload",
74
+ cachedOutput: unchangedOutput,
75
+ changedSteps: changedOutput.steps,
76
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
77
+ };
78
+ }
79
+ return {
80
+ type: "extension-reload",
81
+ cachedOutput: unchangedOutput,
82
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
83
+ };
84
+ }
85
+ function findEffectedSteps(changedFile, currentOutput) {
86
+ const changes = [];
87
+ const changedPath = normalizePath(changedFile[1]);
88
+ const isChunkEffected = (chunk) => (
89
+ // If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
90
+ // fileName is normalized, relative bundle path
91
+ chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
92
+ // moduleIds are absolute, normalized paths
93
+ chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
94
+ );
95
+ for (const step of currentOutput.steps) {
96
+ const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
97
+ if (effectedChunk)
98
+ changes.push(step);
99
+ }
100
+ const effectedAsset = currentOutput.publicAssets.find(
101
+ (chunk) => isChunkEffected(chunk)
102
+ );
103
+ if (effectedAsset)
104
+ changes.push(effectedAsset);
105
+ return changes;
106
+ }
107
+
19
108
  // src/core/utils/entrypoints.ts
109
+ import path, { relative, resolve } from "node:path";
20
110
  function getEntrypointName(entrypointsDir, inputPath) {
21
111
  const relativePath = path.relative(entrypointsDir, inputPath);
22
112
  const name = relativePath.split(/[\.\/\\]/, 2)[0];
@@ -36,87 +126,69 @@ function resolvePerBrowserOption(option, browser) {
36
126
  return option;
37
127
  }
38
128
 
39
- // src/core/utils/time.ts
40
- function formatDuration(duration) {
41
- if (duration < 1e3)
42
- return `${duration} ms`;
43
- if (duration < 1e4)
44
- return `${(duration / 1e3).toFixed(3)} s`;
45
- if (duration < 6e4)
46
- return `${(duration / 1e3).toFixed(1)} s`;
47
- return `${(duration / 1e3).toFixed(0)} s`;
48
- }
49
- function withTimeout(promise, duration) {
50
- return new Promise((res, rej) => {
51
- const timeout = setTimeout(() => {
52
- rej(`Promise timed out after ${duration}ms`);
53
- }, duration);
54
- promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
55
- });
56
- }
129
+ // src/core/utils/building/find-entrypoints.ts
130
+ import { relative as relative4, resolve as resolve12 } from "path";
131
+ import fs12 from "fs-extra";
132
+ import { minimatch } from "minimatch";
133
+ import { parseHTML as parseHTML2 } from "linkedom";
134
+ import JSON5 from "json5";
57
135
 
58
- // src/core/utils/network.ts
59
- import dns from "node:dns";
60
- function isOffline() {
61
- const isOffline2 = new Promise((res) => {
62
- dns.resolve("google.com", (err) => {
63
- if (err == null) {
64
- res(false);
65
- } else {
66
- res(true);
67
- }
68
- });
69
- });
70
- return withTimeout(isOffline2, 1e3).catch(() => true);
136
+ // src/core/utils/fs.ts
137
+ import fs from "fs-extra";
138
+ import glob from "fast-glob";
139
+ async function writeFileIfDifferent(file, newContents) {
140
+ const existingContents = await fs.readFile(file, "utf-8").catch(() => void 0);
141
+ if (existingContents !== newContents) {
142
+ await fs.writeFile(file, newContents);
143
+ }
71
144
  }
72
- async function isOnline() {
73
- const offline = await isOffline();
74
- return !offline;
145
+ async function getPublicFiles(config) {
146
+ if (!await fs.exists(config.publicDir))
147
+ return [];
148
+ const files = await glob("**/*", { cwd: config.publicDir });
149
+ return files.map(unnormalizePath);
75
150
  }
76
- async function fetchCached(url, config) {
77
- let content = "";
78
- if (await isOnline()) {
79
- const res = await fetch(url);
80
- if (res.status < 300) {
81
- content = await res.text();
82
- await config.fsCache.set(url, content);
83
- } else {
84
- config.logger.debug(
85
- `Failed to download "${url}", falling back to cache...`
86
- );
87
- }
151
+
152
+ // src/core/utils/building/build-entrypoints.ts
153
+ import fs2 from "fs-extra";
154
+ import { dirname, resolve as resolve2 } from "path";
155
+ import pc from "picocolors";
156
+ async function buildEntrypoints(groups, config, spinner) {
157
+ const steps = [];
158
+ for (let i = 0; i < groups.length; i++) {
159
+ const group = groups[i];
160
+ const groupNames = [group].flat().map((e) => e.name).join(pc.dim(", "));
161
+ spinner.text = pc.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNames}`;
162
+ steps.push(await config.builder.build(group));
88
163
  }
89
- if (!content)
90
- content = await config.fsCache.get(url) ?? "";
91
- if (!content)
92
- throw Error(
93
- `Offline and "${url}" has not been cached. Try again when online.`
94
- );
95
- return content;
164
+ const publicAssets = await copyPublicDirectory(config);
165
+ return { publicAssets, steps };
96
166
  }
97
-
98
- // src/core/vite-plugins/download.ts
99
- function download(config) {
100
- return {
101
- name: "wxt:download",
102
- resolveId(id) {
103
- if (id.startsWith("url:"))
104
- return "\0" + id;
105
- },
106
- async load(id) {
107
- if (!id.startsWith("\0url:"))
108
- return;
109
- const url = id.replace("\0url:", "");
110
- return await fetchCached(url, config);
111
- }
112
- };
167
+ async function copyPublicDirectory(config) {
168
+ const files = await getPublicFiles(config);
169
+ if (files.length === 0)
170
+ return [];
171
+ const publicAssets = [];
172
+ for (const file of files) {
173
+ const srcPath = resolve2(config.publicDir, file);
174
+ const outPath = resolve2(config.outDir, file);
175
+ await fs2.ensureDir(dirname(outPath));
176
+ await fs2.copyFile(srcPath, outPath);
177
+ publicAssets.push({
178
+ type: "asset",
179
+ fileName: file
180
+ });
181
+ }
182
+ return publicAssets;
113
183
  }
114
184
 
115
- // src/core/vite-plugins/unimport.ts
185
+ // src/core/utils/building/generate-wxt-dir.ts
116
186
  import { createUnimport } from "unimport";
187
+ import fs3 from "fs-extra";
188
+ import { relative as relative2, resolve as resolve3 } from "path";
117
189
 
118
190
  // src/core/utils/unimport.ts
119
- import { mergeConfig } from "vite";
191
+ import { defu } from "defu";
120
192
  function getUnimportOptions(config) {
121
193
  if (config.imports === false)
122
194
  return false;
@@ -135,54 +207,7 @@ function getUnimportOptions(config) {
135
207
  warn: config.logger.warn,
136
208
  dirs: ["components", "composables", "hooks", "utils"]
137
209
  };
138
- return mergeConfig(
139
- defaultOptions,
140
- config.imports
141
- );
142
- }
143
-
144
- // src/core/vite-plugins/unimport.ts
145
- import { extname } from "path";
146
- var ENABLED_EXTENSIONS = /* @__PURE__ */ new Set([
147
- ".js",
148
- ".jsx",
149
- ".ts",
150
- ".tsx",
151
- ".vue",
152
- ".svelte"
153
- ]);
154
- function unimport(config) {
155
- const options = getUnimportOptions(config);
156
- if (options === false)
157
- return [];
158
- const unimport2 = createUnimport(options);
159
- return {
160
- name: "wxt:unimport",
161
- async config() {
162
- await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
163
- },
164
- async transform(code, id) {
165
- if (id.includes("node_modules"))
166
- return;
167
- if (!ENABLED_EXTENSIONS.has(extname(id)))
168
- return;
169
- return unimport2.injectImports(code, id);
170
- }
171
- };
172
- }
173
-
174
- // src/core/vite-plugins/tsconfigPaths.ts
175
- function tsconfigPaths(config) {
176
- return {
177
- name: "wxt:aliases",
178
- async config() {
179
- return {
180
- resolve: {
181
- alias: config.alias
182
- }
183
- };
184
- }
185
- };
210
+ return defu(config.imports, defaultOptions);
186
211
  }
187
212
 
188
213
  // src/core/utils/globals.ts
@@ -230,7 +255,7 @@ function getGlobals(config) {
230
255
  }
231
256
  ];
232
257
  }
233
- function getEntrypointGlobals(config, entrypointName) {
258
+ function getEntrypointGlobals(entrypointName) {
234
259
  return [
235
260
  {
236
261
  name: surroundInUnderscore("ENTRYPOINT"),
@@ -243,68 +268,253 @@ function surroundInUnderscore(name) {
243
268
  return `__${name}__`;
244
269
  }
245
270
 
246
- // src/core/vite-plugins/globals.ts
247
- function globals(config) {
248
- return {
249
- name: "wxt:globals",
250
- config() {
251
- const define = {};
252
- for (const global of getGlobals(config)) {
253
- define[global.name] = JSON.stringify(global.value);
254
- }
255
- return {
256
- define
257
- };
258
- }
259
- };
260
- }
261
-
262
- // src/core/vite-plugins/webextensionPolyfillAlias.ts
271
+ // src/core/utils/building/generate-wxt-dir.ts
263
272
  import path2 from "node:path";
264
- function webextensionPolyfillAlias(config) {
265
- return {
266
- name: "wxt:webextension-polyfill-test-alias",
267
- config() {
268
- return {
269
- resolve: {
270
- alias: {
271
- "webextension-polyfill": path2.resolve(
272
- config.root,
273
- "node_modules/wxt/dist/virtual/mock-browser"
274
- )
275
- }
276
- }
277
- };
273
+
274
+ // src/core/utils/i18n.ts
275
+ var predefinedMessages = {
276
+ "@@extension_id": {
277
+ message: "<browser.runtime.id>",
278
+ description: "The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even unlocalized extensions can use this message.\nNote: You can't use this message in a manifest file."
279
+ },
280
+ "@@ui_locale": {
281
+ message: "<browser.i18n.getUiLocale()>",
282
+ description: ""
283
+ },
284
+ "@@bidi_dir": {
285
+ message: "<ltr|rtl>",
286
+ description: 'The text direction for the current locale, either "ltr" for left-to-right languages such as English or "rtl" for right-to-left languages such as Japanese.'
287
+ },
288
+ "@@bidi_reversed_dir": {
289
+ message: "<rtl|ltr>",
290
+ description: `If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".`
291
+ },
292
+ "@@bidi_start_edge": {
293
+ message: "<left|right>",
294
+ description: `If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".`
295
+ },
296
+ "@@bidi_end_edge": {
297
+ message: "<right|left>",
298
+ description: `If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".`
299
+ }
300
+ };
301
+ function parseI18nMessages(messagesJson) {
302
+ return Object.entries({
303
+ ...predefinedMessages,
304
+ ...messagesJson
305
+ }).map(([name, details]) => ({
306
+ name,
307
+ ...details
308
+ }));
309
+ }
310
+
311
+ // src/core/utils/building/generate-wxt-dir.ts
312
+ async function generateTypesDir(entrypoints, config) {
313
+ await fs3.ensureDir(config.typesDir);
314
+ const references = [];
315
+ const imports = getUnimportOptions(config);
316
+ if (imports !== false) {
317
+ references.push(await writeImportsDeclarationFile(config, imports));
318
+ }
319
+ references.push(await writePathsDeclarationFile(entrypoints, config));
320
+ references.push(await writeI18nDeclarationFile(config));
321
+ references.push(await writeGlobalsDeclarationFile(config));
322
+ const mainReference = await writeMainDeclarationFile(references, config);
323
+ await writeTsConfigFile(mainReference, config);
324
+ }
325
+ async function writeImportsDeclarationFile(config, unimportOptions) {
326
+ const filePath = resolve3(config.typesDir, "imports.d.ts");
327
+ const unimport2 = createUnimport(unimportOptions);
328
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
329
+ await writeFileIfDifferent(
330
+ filePath,
331
+ ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
332
+ "\n"
333
+ ) + "\n"
334
+ );
335
+ return filePath;
336
+ }
337
+ async function writePathsDeclarationFile(entrypoints, config) {
338
+ const filePath = resolve3(config.typesDir, "paths.d.ts");
339
+ const unions = entrypoints.map(
340
+ (entry) => getEntrypointBundlePath(
341
+ entry,
342
+ config.outDir,
343
+ entry.inputPath.endsWith(".html") ? ".html" : ".js"
344
+ )
345
+ ).concat(await getPublicFiles(config)).map(normalizePath).map((path6) => ` | "/${path6}"`).sort().join("\n");
346
+ const template = `// Generated by wxt
347
+ import "wxt/browser";
348
+
349
+ declare module "wxt/browser" {
350
+ export type PublicPath =
351
+ {{ union }}
352
+ export interface WxtRuntime extends Runtime.Static {
353
+ getURL(path: PublicPath): string;
354
+ }
355
+ }
356
+ `;
357
+ await writeFileIfDifferent(
358
+ filePath,
359
+ template.replace("{{ union }}", unions || " | never")
360
+ );
361
+ return filePath;
362
+ }
363
+ async function writeI18nDeclarationFile(config) {
364
+ const filePath = resolve3(config.typesDir, "i18n.d.ts");
365
+ const defaultLocale = config.manifest.default_locale;
366
+ const template = `// Generated by wxt
367
+ import "wxt/browser";
368
+
369
+ declare module "wxt/browser" {
370
+ /**
371
+ * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
372
+ */
373
+ interface GetMessageOptions {
374
+ /**
375
+ * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
376
+ */
377
+ escapeLt?: boolean
378
+ }
379
+
380
+ export interface WxtI18n extends I18n.Static {
381
+ {{ overrides }}
382
+ }
383
+ }
384
+ `;
385
+ let messages;
386
+ if (defaultLocale) {
387
+ const defaultLocalePath = path2.resolve(
388
+ config.publicDir,
389
+ "_locales",
390
+ defaultLocale,
391
+ "messages.json"
392
+ );
393
+ const content = JSON.parse(await fs3.readFile(defaultLocalePath, "utf-8"));
394
+ messages = parseI18nMessages(content);
395
+ } else {
396
+ messages = parseI18nMessages({});
397
+ }
398
+ const overrides = messages.map((message) => {
399
+ return ` /**
400
+ * ${message.description ?? "No message description."}
401
+ *
402
+ * "${message.message}"
403
+ */
404
+ getMessage(
405
+ messageName: "${message.name}",
406
+ substitutions?: string | string[],
407
+ options?: GetMessageOptions,
408
+ ): string;`;
409
+ });
410
+ await writeFileIfDifferent(
411
+ filePath,
412
+ template.replace("{{ overrides }}", overrides.join("\n"))
413
+ );
414
+ return filePath;
415
+ }
416
+ async function writeGlobalsDeclarationFile(config) {
417
+ const filePath = resolve3(config.typesDir, "globals.d.ts");
418
+ const globals2 = [...getGlobals(config), ...getEntrypointGlobals("")];
419
+ await writeFileIfDifferent(
420
+ filePath,
421
+ [
422
+ "// Generated by wxt",
423
+ "export {}",
424
+ "declare global {",
425
+ ...globals2.map((global) => ` const ${global.name}: ${global.type};`),
426
+ "}"
427
+ ].join("\n") + "\n"
428
+ );
429
+ return filePath;
430
+ }
431
+ async function writeMainDeclarationFile(references, config) {
432
+ const dir = config.wxtDir;
433
+ const filePath = resolve3(dir, "wxt.d.ts");
434
+ await writeFileIfDifferent(
435
+ filePath,
436
+ [
437
+ "// Generated by wxt",
438
+ `/// <reference types="vite/client" />`,
439
+ ...references.map(
440
+ (ref) => `/// <reference types="./${normalizePath(relative2(dir, ref))}" />`
441
+ )
442
+ ].join("\n") + "\n"
443
+ );
444
+ return filePath;
445
+ }
446
+ async function writeTsConfigFile(mainReference, config) {
447
+ const dir = config.wxtDir;
448
+ const getTsconfigPath = (path6) => normalizePath(relative2(dir, path6));
449
+ const paths = Object.entries(config.alias).flatMap(([alias, absolutePath]) => {
450
+ const aliasPath = getTsconfigPath(absolutePath);
451
+ return [
452
+ ` "${alias}": ["${aliasPath}"]`,
453
+ ` "${alias}/*": ["${aliasPath}/*"]`
454
+ ];
455
+ }).join(",\n");
456
+ await writeFileIfDifferent(
457
+ resolve3(dir, "tsconfig.json"),
458
+ `{
459
+ "compilerOptions": {
460
+ "target": "ESNext",
461
+ "module": "ESNext",
462
+ "moduleResolution": "Bundler",
463
+ "noEmit": true,
464
+ "esModuleInterop": true,
465
+ "forceConsistentCasingInFileNames": true,
466
+ "resolveJsonModule": true,
467
+ "strict": true,
468
+ "skipLibCheck": true,
469
+ "paths": {
470
+ ${paths}
278
471
  }
279
- };
472
+ },
473
+ "include": [
474
+ "${getTsconfigPath(config.root)}/**/*",
475
+ "./${getTsconfigPath(mainReference)}"
476
+ ],
477
+ "exclude": ["${getTsconfigPath(config.outBaseDir)}"]
478
+ }`
479
+ );
280
480
  }
281
481
 
282
- // src/core/vite-plugins/webextensionPolyfillInlineDeps.ts
283
- function webextensionPolyfillInlineDeps() {
482
+ // src/core/utils/building/get-internal-config.ts
483
+ import { loadConfig } from "c12";
484
+ import path4 from "node:path";
485
+
486
+ // src/core/utils/cache.ts
487
+ import fs4, { ensureDir } from "fs-extra";
488
+ import { dirname as dirname2, resolve as resolve4 } from "path";
489
+ function createFsCache(wxtDir) {
490
+ const getPath = (key) => resolve4(wxtDir, "cache", encodeURIComponent(key));
284
491
  return {
285
- name: "wxt:testing-inline-deps",
286
- config() {
287
- const wxtModules = ["wxt/browser", "wxt/client"];
288
- return {
289
- test: {
290
- server: {
291
- deps: {
292
- inline: [...wxtModules]
293
- }
294
- }
295
- }
296
- };
492
+ async set(key, value) {
493
+ const path6 = getPath(key);
494
+ await ensureDir(dirname2(path6));
495
+ await writeFileIfDifferent(path6, value);
496
+ },
497
+ async get(key) {
498
+ const path6 = getPath(key);
499
+ try {
500
+ return await fs4.readFile(path6, "utf-8");
501
+ } catch {
502
+ return void 0;
503
+ }
297
504
  }
298
505
  };
299
506
  }
300
507
 
301
- // src/core/vite-plugins/devHtmlPrerender.ts
508
+ // src/core/utils/building/get-internal-config.ts
509
+ import consola, { LogLevels } from "consola";
510
+
511
+ // src/core/builders/vite/plugins/devHtmlPrerender.ts
302
512
  import { parseHTML } from "linkedom";
303
- import { dirname, isAbsolute, relative as relative2, resolve as resolve2 } from "path";
513
+ import { dirname as dirname3, isAbsolute, relative as relative3, resolve as resolve5 } from "node:path";
304
514
  var reactRefreshPreamble = "";
305
515
  function devHtmlPrerender(config) {
306
516
  const htmlReloadId = "@wxt/reload-html";
307
- const resolvedHtmlReloadId = resolve2(
517
+ const resolvedHtmlReloadId = resolve5(
308
518
  config.root,
309
519
  "node_modules/wxt/dist/virtual/reload-html.js"
310
520
  );
@@ -338,8 +548,8 @@ function devHtmlPrerender(config) {
338
548
  if (isAbsolute(src)) {
339
549
  element.setAttribute(attr, server.origin + src);
340
550
  } else if (src.startsWith(".")) {
341
- const abs = resolve2(dirname(id), src);
342
- const pathname = relative2(config.root, abs);
551
+ const abs = resolve5(dirname3(id), src);
552
+ const pathname = relative3(config.root, abs);
343
553
  element.setAttribute(attr, `${server.origin}/${pathname}`);
344
554
  }
345
555
  });
@@ -364,11 +574,7 @@ function devHtmlPrerender(config) {
364
574
  const originalUrl = `${server.origin}${ctx.path}`;
365
575
  const name = getEntrypointName(config.entrypointsDir, ctx.filename);
366
576
  const url = `${server.origin}/${name}.html`;
367
- const serverHtml = await server.transformIndexHtml(
368
- url,
369
- html,
370
- originalUrl
371
- );
577
+ const serverHtml = await server.transformHtml(url, html, originalUrl);
372
578
  const { document } = parseHTML(serverHtml);
373
579
  const reactRefreshScript = Array.from(
374
580
  document.querySelectorAll("script[type=module]")
@@ -416,59 +622,135 @@ function devHtmlPrerender(config) {
416
622
  ];
417
623
  }
418
624
 
419
- // src/core/vite-plugins/devServerGlobals.ts
420
- function devServerGlobals(internalConfig) {
625
+ // src/core/builders/vite/plugins/devServerGlobals.ts
626
+ function devServerGlobals(config) {
421
627
  return {
422
628
  name: "wxt:dev-server-globals",
423
629
  config() {
424
- if (internalConfig.server == null || internalConfig.command == "build")
630
+ if (config.server == null || config.command == "build")
425
631
  return;
426
632
  return {
427
633
  define: {
428
634
  __DEV_SERVER_PROTOCOL__: JSON.stringify("ws:"),
429
- __DEV_SERVER_HOSTNAME__: JSON.stringify(
430
- internalConfig.server.hostname
431
- ),
432
- __DEV_SERVER_PORT__: JSON.stringify(internalConfig.server.port)
635
+ __DEV_SERVER_HOSTNAME__: JSON.stringify(config.server.hostname),
636
+ __DEV_SERVER_PORT__: JSON.stringify(config.server.port)
433
637
  }
434
638
  };
435
639
  }
436
640
  };
437
641
  }
438
642
 
439
- // src/core/vite-plugins/multipageMove.ts
440
- import { dirname as dirname2, extname as extname2, resolve as resolve3 } from "node:path";
441
- import fs, { ensureDir } from "fs-extra";
442
- function multipageMove(entrypoints, config) {
443
- return {
444
- name: "wxt:multipage-move",
445
- async writeBundle(_, bundle) {
446
- for (const oldBundlePath in bundle) {
447
- const entrypoint = entrypoints.find(
448
- (entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath)
449
- );
450
- if (entrypoint == null) {
451
- config.logger.debug(
452
- `No entrypoint found for ${oldBundlePath}, leaving in chunks directory`
453
- );
454
- continue;
455
- }
456
- const newBundlePath = getEntrypointBundlePath(
457
- entrypoint,
458
- config.outDir,
459
- extname2(oldBundlePath)
460
- );
461
- if (newBundlePath === oldBundlePath) {
462
- config.logger.debug(
643
+ // src/core/utils/network.ts
644
+ import dns from "node:dns";
645
+
646
+ // src/core/utils/time.ts
647
+ function formatDuration(duration) {
648
+ if (duration < 1e3)
649
+ return `${duration} ms`;
650
+ if (duration < 1e4)
651
+ return `${(duration / 1e3).toFixed(3)} s`;
652
+ if (duration < 6e4)
653
+ return `${(duration / 1e3).toFixed(1)} s`;
654
+ return `${(duration / 1e3).toFixed(0)} s`;
655
+ }
656
+ function withTimeout(promise, duration) {
657
+ return new Promise((res, rej) => {
658
+ const timeout = setTimeout(() => {
659
+ rej(`Promise timed out after ${duration}ms`);
660
+ }, duration);
661
+ promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
662
+ });
663
+ }
664
+
665
+ // src/core/utils/network.ts
666
+ function isOffline() {
667
+ const isOffline2 = new Promise((res) => {
668
+ dns.resolve("google.com", (err) => {
669
+ if (err == null) {
670
+ res(false);
671
+ } else {
672
+ res(true);
673
+ }
674
+ });
675
+ });
676
+ return withTimeout(isOffline2, 1e3).catch(() => true);
677
+ }
678
+ async function isOnline() {
679
+ const offline = await isOffline();
680
+ return !offline;
681
+ }
682
+ async function fetchCached(url, config) {
683
+ let content = "";
684
+ if (await isOnline()) {
685
+ const res = await fetch(url);
686
+ if (res.status < 300) {
687
+ content = await res.text();
688
+ await config.fsCache.set(url, content);
689
+ } else {
690
+ config.logger.debug(
691
+ `Failed to download "${url}", falling back to cache...`
692
+ );
693
+ }
694
+ }
695
+ if (!content)
696
+ content = await config.fsCache.get(url) ?? "";
697
+ if (!content)
698
+ throw Error(
699
+ `Offline and "${url}" has not been cached. Try again when online.`
700
+ );
701
+ return content;
702
+ }
703
+
704
+ // src/core/builders/vite/plugins/download.ts
705
+ function download(config) {
706
+ return {
707
+ name: "wxt:download",
708
+ resolveId(id) {
709
+ if (id.startsWith("url:"))
710
+ return "\0" + id;
711
+ },
712
+ async load(id) {
713
+ if (!id.startsWith("\0url:"))
714
+ return;
715
+ const url = id.replace("\0url:", "");
716
+ return await fetchCached(url, config);
717
+ }
718
+ };
719
+ }
720
+
721
+ // src/core/builders/vite/plugins/multipageMove.ts
722
+ import { dirname as dirname4, extname, resolve as resolve6, join } from "node:path";
723
+ import fs5, { ensureDir as ensureDir2 } from "fs-extra";
724
+ function multipageMove(entrypoints, config) {
725
+ return {
726
+ name: "wxt:multipage-move",
727
+ async writeBundle(_, bundle) {
728
+ for (const oldBundlePath in bundle) {
729
+ const entrypoint = entrypoints.find(
730
+ (entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath)
731
+ );
732
+ if (entrypoint == null) {
733
+ config.logger.debug(
734
+ `No entrypoint found for ${oldBundlePath}, leaving in chunks directory`
735
+ );
736
+ continue;
737
+ }
738
+ const newBundlePath = getEntrypointBundlePath(
739
+ entrypoint,
740
+ config.outDir,
741
+ extname(oldBundlePath)
742
+ );
743
+ if (newBundlePath === oldBundlePath) {
744
+ config.logger.debug(
463
745
  "HTML file is already in the correct location",
464
746
  oldBundlePath
465
747
  );
466
748
  continue;
467
749
  }
468
- const oldAbsPath = resolve3(config.outDir, oldBundlePath);
469
- const newAbsPath = resolve3(config.outDir, newBundlePath);
470
- await ensureDir(dirname2(newAbsPath));
471
- await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
750
+ const oldAbsPath = resolve6(config.outDir, oldBundlePath);
751
+ const newAbsPath = resolve6(config.outDir, newBundlePath);
752
+ await ensureDir2(dirname4(newAbsPath));
753
+ await fs5.move(oldAbsPath, newAbsPath, { overwrite: true });
472
754
  const renamedChunk = {
473
755
  ...bundle[oldBundlePath],
474
756
  fileName: newBundlePath
@@ -476,13 +758,59 @@ function multipageMove(entrypoints, config) {
476
758
  delete bundle[oldBundlePath];
477
759
  bundle[newBundlePath] = renamedChunk;
478
760
  }
761
+ removeEmptyDirs(config.outDir);
479
762
  }
480
763
  };
481
764
  }
765
+ async function removeEmptyDirs(dir) {
766
+ const files = await fs5.readdir(dir);
767
+ for (const file of files) {
768
+ const filePath = join(dir, file);
769
+ const stats = await fs5.stat(filePath);
770
+ if (stats.isDirectory()) {
771
+ await removeEmptyDirs(filePath);
772
+ }
773
+ }
774
+ try {
775
+ await fs5.rmdir(dir);
776
+ } catch {
777
+ }
778
+ }
482
779
 
483
- // src/core/vite-plugins/virtualEntrypoint.ts
484
- import fs2 from "fs-extra";
485
- import { resolve as resolve4 } from "path";
780
+ // src/core/builders/vite/plugins/unimport.ts
781
+ import { createUnimport as createUnimport2 } from "unimport";
782
+ import { extname as extname2 } from "path";
783
+ var ENABLED_EXTENSIONS = /* @__PURE__ */ new Set([
784
+ ".js",
785
+ ".jsx",
786
+ ".ts",
787
+ ".tsx",
788
+ ".vue",
789
+ ".svelte"
790
+ ]);
791
+ function unimport(config) {
792
+ const options = getUnimportOptions(config);
793
+ if (options === false)
794
+ return [];
795
+ const unimport2 = createUnimport2(options);
796
+ return {
797
+ name: "wxt:unimport",
798
+ async config() {
799
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
800
+ },
801
+ async transform(code, id) {
802
+ if (id.includes("node_modules"))
803
+ return;
804
+ if (!ENABLED_EXTENSIONS.has(extname2(id)))
805
+ return;
806
+ return unimport2.injectImports(code, id);
807
+ }
808
+ };
809
+ }
810
+
811
+ // src/core/builders/vite/plugins/virtualEntrypoint.ts
812
+ import fs6 from "fs-extra";
813
+ import { resolve as resolve7 } from "path";
486
814
  function virtualEntrypoint(type, config) {
487
815
  const virtualId = `virtual:wxt-${type}?`;
488
816
  const resolvedVirtualId = `\0${virtualId}`;
@@ -499,8 +827,8 @@ function virtualEntrypoint(type, config) {
499
827
  if (!id.startsWith(resolvedVirtualId))
500
828
  return;
501
829
  const inputPath = id.replace(resolvedVirtualId, "");
502
- const template = await fs2.readFile(
503
- resolve4(
830
+ const template = await fs6.readFile(
831
+ resolve7(
504
832
  config.root,
505
833
  `node_modules/wxt/dist/virtual/${type}-entrypoint.js`
506
834
  ),
@@ -511,7 +839,24 @@ function virtualEntrypoint(type, config) {
511
839
  };
512
840
  }
513
841
 
514
- // src/core/vite-plugins/noopBackground.ts
842
+ // src/core/builders/vite/plugins/tsconfigPaths.ts
843
+ function tsconfigPaths(config) {
844
+ return {
845
+ name: "wxt:aliases",
846
+ async config() {
847
+ return {
848
+ resolve: {
849
+ alias: config.alias
850
+ }
851
+ };
852
+ }
853
+ };
854
+ }
855
+
856
+ // src/core/utils/constants.ts
857
+ var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
858
+
859
+ // src/core/builders/vite/plugins/noopBackground.ts
515
860
  function noopBackground() {
516
861
  const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
517
862
  const resolvedVirtualModuleId = "\0" + virtualModuleId;
@@ -529,9 +874,8 @@ export default defineBackground(() => void 0)`;
529
874
  }
530
875
  };
531
876
  }
532
- var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
533
877
 
534
- // src/core/vite-plugins/cssEntrypoints.ts
878
+ // src/core/builders/vite/plugins/cssEntrypoints.ts
535
879
  function cssEntrypoints(entrypoint, config) {
536
880
  return {
537
881
  name: "wxt:css-entrypoint",
@@ -543,570 +887,322 @@ function cssEntrypoints(entrypoint, config) {
543
887
  assetFileNames: () => getEntrypointBundlePath(entrypoint, config.outDir, ".css")
544
888
  }
545
889
  }
546
- }
547
- };
548
- },
549
- generateBundle(_, bundle) {
550
- Object.keys(bundle).forEach((file) => {
551
- if (file.endsWith(".js"))
552
- delete bundle[file];
553
- });
554
- }
555
- };
556
- }
557
-
558
- // src/core/vite-plugins/bundleAnalysis.ts
559
- import { visualizer } from "rollup-plugin-visualizer";
560
- var increment = 0;
561
- function bundleAnalysis() {
562
- return visualizer({
563
- emitFile: true,
564
- template: "raw-data",
565
- filename: `stats-${increment++}.json`
566
- });
567
- }
568
-
569
- // src/core/vite-plugins/excludeBrowserPolyfill.ts
570
- function excludeBrowserPolyfill(config) {
571
- const virtualId = "virtual:wxt-webextension-polyfill-disabled";
572
- return {
573
- name: "wxt:exclude-browser-polyfill",
574
- config() {
575
- if (config.experimental.includeBrowserPolyfill)
576
- return;
577
- return {
578
- resolve: {
579
- alias: {
580
- "webextension-polyfill": virtualId
581
- }
582
- }
583
- };
584
- },
585
- load(id) {
586
- if (id === virtualId) {
587
- return "export default chrome";
588
- }
589
- }
590
- };
591
- }
592
-
593
- // src/core/utils/arrays.ts
594
- function every(array, predicate) {
595
- for (let i = 0; i < array.length; i++)
596
- if (!predicate(array[i], i))
597
- return false;
598
- return true;
599
- }
600
-
601
- // src/core/utils/building/detect-dev-changes.ts
602
- function detectDevChanges(changedFiles, currentOutput) {
603
- if (currentOutput == null)
604
- return { type: "no-change" };
605
- const changedSteps = new Set(
606
- changedFiles.flatMap(
607
- (changedFile) => findEffectedSteps(changedFile, currentOutput)
608
- )
609
- );
610
- if (changedSteps.size === 0)
611
- return { type: "no-change" };
612
- const unchangedOutput = {
613
- manifest: currentOutput.manifest,
614
- steps: [],
615
- publicAssets: []
616
- };
617
- const changedOutput = {
618
- manifest: currentOutput.manifest,
619
- steps: [],
620
- publicAssets: []
621
- };
622
- for (const step of currentOutput.steps) {
623
- if (changedSteps.has(step)) {
624
- changedOutput.steps.push(step);
625
- } else {
626
- unchangedOutput.steps.push(step);
627
- }
628
- }
629
- for (const asset of currentOutput.publicAssets) {
630
- if (changedSteps.has(asset)) {
631
- changedOutput.publicAssets.push(asset);
632
- } else {
633
- unchangedOutput.publicAssets.push(asset);
634
- }
635
- }
636
- const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, ([_, file]) => file.endsWith(".html"));
637
- if (isOnlyHtmlChanges) {
638
- return {
639
- type: "html-reload",
640
- cachedOutput: unchangedOutput,
641
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
642
- };
643
- }
644
- const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
645
- changedOutput.steps.flatMap((step) => step.entrypoints),
646
- (entry) => entry.type === "content-script"
647
- );
648
- if (isOnlyContentScripts) {
649
- return {
650
- type: "content-script-reload",
651
- cachedOutput: unchangedOutput,
652
- changedSteps: changedOutput.steps,
653
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
654
- };
655
- }
656
- return {
657
- type: "extension-reload",
658
- cachedOutput: unchangedOutput,
659
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
660
- };
661
- }
662
- function findEffectedSteps(changedFile, currentOutput) {
663
- const changes = [];
664
- const changedPath = normalizePath(changedFile[1]);
665
- const isChunkEffected = (chunk) => (
666
- // If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
667
- // fileName is normalized, relative bundle path
668
- chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
669
- // moduleIds are absolute, normalized paths
670
- chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
671
- );
672
- for (const step of currentOutput.steps) {
673
- const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
674
- if (effectedChunk)
675
- changes.push(step);
676
- }
677
- const effectedAsset = currentOutput.publicAssets.find(
678
- (chunk) => isChunkEffected(chunk)
679
- );
680
- if (effectedAsset)
681
- changes.push(effectedAsset);
682
- return changes;
683
- }
684
-
685
- // src/core/utils/building/find-entrypoints.ts
686
- import { relative as relative4, resolve as resolve12 } from "path";
687
- import fs12 from "fs-extra";
688
- import { minimatch } from "minimatch";
689
- import { parseHTML as parseHTML2 } from "linkedom";
690
- import JSON5 from "json5";
691
-
692
- // src/core/utils/building/build-entrypoints.ts
693
- import * as vite from "vite";
694
-
695
- // src/core/utils/fs.ts
696
- import fs3 from "fs-extra";
697
- import glob from "fast-glob";
698
- import path3 from "node:path";
699
- async function writeFileIfDifferent(file, newContents) {
700
- const existingContents = await fs3.readFile(file, "utf-8").catch(() => void 0);
701
- if (existingContents !== newContents) {
702
- await fs3.writeFile(file, newContents);
703
- }
704
- }
705
- async function getPublicFiles(config) {
706
- if (!await fs3.exists(config.publicDir))
707
- return [];
708
- const files = await glob("**/*", { cwd: config.publicDir });
709
- return files.map(unnormalizePath);
710
- }
711
- async function removeEmptyDirs(dir) {
712
- const files = await fs3.readdir(dir);
713
- for (const file of files) {
714
- const filePath = path3.join(dir, file);
715
- const stats = await fs3.stat(filePath);
716
- if (stats.isDirectory()) {
717
- await removeEmptyDirs(filePath);
718
- }
719
- }
720
- try {
721
- await fs3.rmdir(dir);
722
- } catch {
723
- }
724
- }
725
-
726
- // src/core/utils/building/build-entrypoints.ts
727
- import fs4 from "fs-extra";
728
- import { dirname as dirname3, resolve as resolve5 } from "path";
729
- import pc from "picocolors";
730
- async function buildEntrypoints(groups, config, spinner) {
731
- const steps = [];
732
- for (let i = 0; i < groups.length; i++) {
733
- const group = groups[i];
734
- spinner.text = pc.dim(`[${i + 1}/${groups.length}]`) + ` ${[group].flat().map((e) => e.name).join(pc.dim(", "))}`;
735
- const step = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config);
736
- steps.push(step);
737
- }
738
- const publicAssets = await copyPublicDirectory(config);
739
- await removeEmptyDirs(config.outDir);
740
- return { publicAssets, steps };
741
- }
742
- async function buildSingleEntrypoint(entrypoint, config) {
743
- const isVirtual = [
744
- "background",
745
- "content-script",
746
- "unlisted-script"
747
- ].includes(entrypoint.type);
748
- const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
749
- const plugins = [];
750
- if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
751
- plugins.push(cssEntrypoints(entrypoint, config));
752
- }
753
- const libMode = {
754
- mode: config.mode,
755
- plugins,
756
- build: {
757
- lib: {
758
- entry,
759
- formats: ["iife"],
760
- name: "_",
761
- fileName: entrypoint.name
762
- },
763
- rollupOptions: {
764
- output: {
765
- // There's only a single output for this build, so we use the desired bundle path for the
766
- // entry output (like "content-scripts/overlay.js")
767
- entryFileNames: getEntrypointBundlePath(
768
- entrypoint,
769
- config.outDir,
770
- ".js"
771
- ),
772
- // Output content script CSS to `content-scripts/`, but all other scripts are written to
773
- // `assets/`.
774
- assetFileNames: ({ name }) => {
775
- if (entrypoint.type === "content-script" && name?.endsWith("css")) {
776
- return `content-scripts/${entrypoint.name}.[ext]`;
777
- } else {
778
- return `assets/${entrypoint.name}.[ext]`;
779
- }
780
- }
781
- }
782
- }
783
- },
784
- define: {
785
- // See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
786
- "process.env.NODE_ENV": JSON.stringify(config.mode)
787
- }
788
- };
789
- for (const global of getEntrypointGlobals(config, entrypoint.name)) {
790
- libMode.define[global.name] = JSON.stringify(global.value);
791
- }
792
- const entryConfig = vite.mergeConfig(
793
- libMode,
794
- await config.vite(config.env)
795
- );
796
- const result = await vite.build(entryConfig);
797
- return {
798
- entrypoints: entrypoint,
799
- chunks: getBuildOutputChunks(result)
800
- };
801
- }
802
- async function buildMultipleEntrypoints(entrypoints, config) {
803
- const multiPage = {
804
- mode: config.mode,
805
- plugins: [multipageMove(entrypoints, config)],
806
- build: {
807
- rollupOptions: {
808
- input: entrypoints.reduce((input, entry) => {
809
- input[entry.name] = entry.inputPath;
810
- return input;
811
- }, {}),
812
- output: {
813
- // Include a hash to prevent conflicts
814
- chunkFileNames: "chunks/[name]-[hash].js",
815
- // Include a hash to prevent conflicts
816
- entryFileNames: "chunks/[name]-[hash].js",
817
- // We can't control the "name", so we need a hash to prevent conflicts
818
- assetFileNames: "assets/[name]-[hash].[ext]"
819
- }
820
- }
821
- },
822
- define: {}
823
- };
824
- for (const global of getEntrypointGlobals(config, "html")) {
825
- multiPage.define[global.name] = JSON.stringify(global.value);
826
- }
827
- const entryConfig = vite.mergeConfig(
828
- multiPage,
829
- await config.vite(config.env)
830
- );
831
- const result = await vite.build(entryConfig);
832
- return {
833
- entrypoints,
834
- chunks: getBuildOutputChunks(result)
835
- };
836
- }
837
- function getBuildOutputChunks(result) {
838
- if ("on" in result)
839
- throw Error("wxt does not support vite watch mode.");
840
- if (Array.isArray(result))
841
- return result.flatMap(({ output }) => output);
842
- return result.output;
843
- }
844
- async function copyPublicDirectory(config) {
845
- const files = await getPublicFiles(config);
846
- if (files.length === 0)
847
- return [];
848
- const publicAssets = [];
849
- for (const file of files) {
850
- const srcPath = resolve5(config.publicDir, file);
851
- const outPath = resolve5(config.outDir, file);
852
- await fs4.ensureDir(dirname3(outPath));
853
- await fs4.copyFile(srcPath, outPath);
854
- publicAssets.push({
855
- type: "asset",
856
- fileName: file,
857
- name: file,
858
- needsCodeReference: false,
859
- source: await fs4.readFile(srcPath)
860
- });
861
- }
862
- return publicAssets;
863
- }
864
-
865
- // src/core/utils/building/generate-wxt-dir.ts
866
- import { createUnimport as createUnimport2 } from "unimport";
867
- import fs5 from "fs-extra";
868
- import { relative as relative3, resolve as resolve6 } from "path";
869
- import path4 from "node:path";
870
-
871
- // src/core/utils/i18n.ts
872
- var predefinedMessages = {
873
- "@@extension_id": {
874
- message: "<browser.runtime.id>",
875
- description: "The extension or app ID; you might use this string to construct URLs for resources inside the extension. Even unlocalized extensions can use this message.\nNote: You can't use this message in a manifest file."
876
- },
877
- "@@ui_locale": {
878
- message: "<browser.i18n.getUiLocale()>",
879
- description: ""
880
- },
881
- "@@bidi_dir": {
882
- message: "<ltr|rtl>",
883
- description: 'The text direction for the current locale, either "ltr" for left-to-right languages such as English or "rtl" for right-to-left languages such as Japanese.'
884
- },
885
- "@@bidi_reversed_dir": {
886
- message: "<rtl|ltr>",
887
- description: `If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".`
888
- },
889
- "@@bidi_start_edge": {
890
- message: "<left|right>",
891
- description: `If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".`
892
- },
893
- "@@bidi_end_edge": {
894
- message: "<right|left>",
895
- description: `If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".`
896
- }
897
- };
898
- function parseI18nMessages(messagesJson) {
899
- return Object.entries({
900
- ...predefinedMessages,
901
- ...messagesJson
902
- }).map(([name, details]) => ({
903
- name,
904
- ...details
905
- }));
906
- }
907
-
908
- // src/core/utils/building/generate-wxt-dir.ts
909
- async function generateTypesDir(entrypoints, config) {
910
- await fs5.ensureDir(config.typesDir);
911
- const references = [];
912
- const imports = getUnimportOptions(config);
913
- if (imports !== false) {
914
- references.push(await writeImportsDeclarationFile(config, imports));
915
- }
916
- references.push(await writePathsDeclarationFile(entrypoints, config));
917
- references.push(await writeI18nDeclarationFile(config));
918
- references.push(await writeGlobalsDeclarationFile(config));
919
- const mainReference = await writeMainDeclarationFile(references, config);
920
- await writeTsConfigFile(mainReference, config);
921
- }
922
- async function writeImportsDeclarationFile(config, unimportOptions) {
923
- const filePath = resolve6(config.typesDir, "imports.d.ts");
924
- const unimport2 = createUnimport2(unimportOptions);
925
- await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
926
- await writeFileIfDifferent(
927
- filePath,
928
- ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
929
- "\n"
930
- ) + "\n"
931
- );
932
- return filePath;
933
- }
934
- async function writePathsDeclarationFile(entrypoints, config) {
935
- const filePath = resolve6(config.typesDir, "paths.d.ts");
936
- const unions = entrypoints.map(
937
- (entry) => getEntrypointBundlePath(
938
- entry,
939
- config.outDir,
940
- entry.inputPath.endsWith(".html") ? ".html" : ".js"
941
- )
942
- ).concat(await getPublicFiles(config)).map(normalizePath).map((path7) => ` | "/${path7}"`).sort().join("\n");
943
- const template = `// Generated by wxt
944
- import "wxt/browser";
945
-
946
- declare module "wxt/browser" {
947
- export type PublicPath =
948
- {{ union }}
949
- export interface WxtRuntime extends Runtime.Static {
950
- getURL(path: PublicPath): string;
951
- }
952
- }
953
- `;
954
- await writeFileIfDifferent(
955
- filePath,
956
- template.replace("{{ union }}", unions || " | never")
957
- );
958
- return filePath;
959
- }
960
- async function writeI18nDeclarationFile(config) {
961
- const filePath = resolve6(config.typesDir, "i18n.d.ts");
962
- const defaultLocale = config.manifest.default_locale;
963
- const template = `// Generated by wxt
964
- import "wxt/browser";
965
-
966
- declare module "wxt/browser" {
967
- /**
968
- * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
969
- */
970
- interface GetMessageOptions {
971
- /**
972
- * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
973
- */
974
- escapeLt?: boolean
975
- }
976
-
977
- export interface WxtI18n extends I18n.Static {
978
- {{ overrides }}
979
- }
890
+ }
891
+ };
892
+ },
893
+ generateBundle(_, bundle) {
894
+ Object.keys(bundle).forEach((file) => {
895
+ if (file.endsWith(".js"))
896
+ delete bundle[file];
897
+ });
898
+ }
899
+ };
980
900
  }
981
- `;
982
- let messages;
983
- if (defaultLocale) {
984
- const defaultLocalePath = path4.resolve(
985
- config.publicDir,
986
- "_locales",
987
- defaultLocale,
988
- "messages.json"
989
- );
990
- const content = JSON.parse(await fs5.readFile(defaultLocalePath, "utf-8"));
991
- messages = parseI18nMessages(content);
992
- } else {
993
- messages = parseI18nMessages({});
994
- }
995
- const overrides = messages.map((message) => {
996
- return ` /**
997
- * ${message.description ?? "No message description."}
998
- *
999
- * "${message.message}"
1000
- */
1001
- getMessage(
1002
- messageName: "${message.name}",
1003
- substitutions?: string | string[],
1004
- options?: GetMessageOptions,
1005
- ): string;`;
901
+
902
+ // src/core/builders/vite/plugins/bundleAnalysis.ts
903
+ import { visualizer } from "rollup-plugin-visualizer";
904
+ var increment = 0;
905
+ function bundleAnalysis() {
906
+ return visualizer({
907
+ emitFile: true,
908
+ template: "raw-data",
909
+ filename: `stats-${increment++}.json`
1006
910
  });
1007
- await writeFileIfDifferent(
1008
- filePath,
1009
- template.replace("{{ overrides }}", overrides.join("\n"))
1010
- );
1011
- return filePath;
1012
- }
1013
- async function writeGlobalsDeclarationFile(config) {
1014
- const filePath = resolve6(config.typesDir, "globals.d.ts");
1015
- const globals2 = [...getGlobals(config), ...getEntrypointGlobals(config, "")];
1016
- await writeFileIfDifferent(
1017
- filePath,
1018
- [
1019
- "// Generated by wxt",
1020
- "export {}",
1021
- "declare global {",
1022
- ...globals2.map((global) => ` const ${global.name}: ${global.type};`),
1023
- "}"
1024
- ].join("\n") + "\n"
1025
- );
1026
- return filePath;
1027
911
  }
1028
- async function writeMainDeclarationFile(references, config) {
1029
- const dir = config.wxtDir;
1030
- const filePath = resolve6(dir, "wxt.d.ts");
1031
- await writeFileIfDifferent(
1032
- filePath,
1033
- [
1034
- "// Generated by wxt",
1035
- `/// <reference types="vite/client" />`,
1036
- ...references.map(
1037
- (ref) => `/// <reference types="./${normalizePath(relative3(dir, ref))}" />`
1038
- )
1039
- ].join("\n") + "\n"
1040
- );
1041
- return filePath;
912
+
913
+ // src/core/builders/vite/plugins/globals.ts
914
+ function globals(config) {
915
+ return {
916
+ name: "wxt:globals",
917
+ config() {
918
+ const define = {};
919
+ for (const global of getGlobals(config)) {
920
+ define[global.name] = JSON.stringify(global.value);
921
+ }
922
+ return {
923
+ define
924
+ };
925
+ }
926
+ };
1042
927
  }
1043
- async function writeTsConfigFile(mainReference, config) {
1044
- const dir = config.wxtDir;
1045
- const getTsconfigPath = (path7) => normalizePath(relative3(dir, path7));
1046
- const paths = Object.entries(config.alias).flatMap(([alias, absolutePath]) => {
1047
- const aliasPath = getTsconfigPath(absolutePath);
1048
- return [
1049
- ` "${alias}": ["${aliasPath}"]`,
1050
- ` "${alias}/*": ["${aliasPath}/*"]`
1051
- ];
1052
- }).join(",\n");
1053
- await writeFileIfDifferent(
1054
- resolve6(dir, "tsconfig.json"),
1055
- `{
1056
- "compilerOptions": {
1057
- "target": "ESNext",
1058
- "module": "ESNext",
1059
- "moduleResolution": "Bundler",
1060
- "noEmit": true,
1061
- "esModuleInterop": true,
1062
- "forceConsistentCasingInFileNames": true,
1063
- "resolveJsonModule": true,
1064
- "strict": true,
1065
- "skipLibCheck": true,
1066
- "paths": {
1067
- ${paths}
928
+
929
+ // src/core/builders/vite/plugins/webextensionPolyfillAlias.ts
930
+ import path3 from "node:path";
931
+ function webextensionPolyfillAlias(config) {
932
+ return {
933
+ name: "wxt:webextension-polyfill-test-alias",
934
+ config() {
935
+ return {
936
+ resolve: {
937
+ alias: {
938
+ "webextension-polyfill": path3.resolve(
939
+ config.root,
940
+ "node_modules/wxt/dist/virtual/mock-browser"
941
+ )
942
+ }
943
+ }
944
+ };
1068
945
  }
1069
- },
1070
- "include": [
1071
- "${getTsconfigPath(config.root)}/**/*",
1072
- "./${getTsconfigPath(mainReference)}"
1073
- ],
1074
- "exclude": ["${getTsconfigPath(config.outBaseDir)}"]
1075
- }`
1076
- );
946
+ };
1077
947
  }
1078
948
 
1079
- // src/core/utils/building/get-internal-config.ts
1080
- import { loadConfig } from "c12";
1081
- import path5 from "node:path";
1082
- import * as vite2 from "vite";
949
+ // src/core/builders/vite/plugins/webextensionPolyfillInlineDeps.ts
950
+ function webextensionPolyfillInlineDeps() {
951
+ return {
952
+ name: "wxt:testing-inline-deps",
953
+ config() {
954
+ const wxtModules = ["wxt/browser", "wxt/client"];
955
+ return {
956
+ test: {
957
+ server: {
958
+ deps: {
959
+ inline: [...wxtModules]
960
+ }
961
+ }
962
+ }
963
+ };
964
+ }
965
+ };
966
+ }
1083
967
 
1084
- // src/core/utils/cache.ts
1085
- import fs6, { ensureDir as ensureDir2 } from "fs-extra";
1086
- import { dirname as dirname4, resolve as resolve7 } from "path";
1087
- function createFsCache(wxtDir) {
1088
- const getPath = (key) => resolve7(wxtDir, "cache", encodeURIComponent(key));
968
+ // src/core/builders/vite/plugins/excludeBrowserPolyfill.ts
969
+ function excludeBrowserPolyfill(config) {
970
+ const virtualId = "virtual:wxt-webextension-polyfill-disabled";
1089
971
  return {
1090
- async set(key, value) {
1091
- const path7 = getPath(key);
1092
- await ensureDir2(dirname4(path7));
1093
- await writeFileIfDifferent(path7, value);
972
+ name: "wxt:exclude-browser-polyfill",
973
+ config() {
974
+ if (config.experimental.includeBrowserPolyfill)
975
+ return;
976
+ return {
977
+ resolve: {
978
+ alias: {
979
+ "webextension-polyfill": virtualId
980
+ }
981
+ }
982
+ };
1094
983
  },
1095
- async get(key) {
1096
- const path7 = getPath(key);
1097
- try {
1098
- return await fs6.readFile(path7, "utf-8");
1099
- } catch {
1100
- return void 0;
984
+ load(id) {
985
+ if (id === virtualId) {
986
+ return "export default chrome";
987
+ }
988
+ }
989
+ };
990
+ }
991
+
992
+ // src/core/builders/vite/plugins/entrypointGroupGlobals.ts
993
+ function entrypointGroupGlobals(entrypointGroup) {
994
+ return {
995
+ name: "wxt:entrypoint-group-globals",
996
+ config() {
997
+ const define = {};
998
+ let name = Array.isArray(entrypointGroup) ? "html" : entrypointGroup.name;
999
+ for (const global of getEntrypointGlobals(name)) {
1000
+ define[global.name] = JSON.stringify(global.value);
1001
+ }
1002
+ return {
1003
+ define
1004
+ };
1005
+ }
1006
+ };
1007
+ }
1008
+
1009
+ // src/core/builders/vite/index.ts
1010
+ async function craeteViteBuilder(inlineConfig, userConfig, wxtConfig) {
1011
+ const vite = await import("vite");
1012
+ const getBaseConfig = async () => {
1013
+ const resolvedInlineConfig = await inlineConfig.vite?.(wxtConfig.env) ?? {};
1014
+ const resolvedUserConfig = await userConfig.vite?.(wxtConfig.env) ?? {};
1015
+ const config = vite.mergeConfig(
1016
+ resolvedUserConfig,
1017
+ resolvedInlineConfig
1018
+ );
1019
+ config.root = wxtConfig.root;
1020
+ config.configFile = false;
1021
+ config.logLevel = "warn";
1022
+ config.mode = wxtConfig.mode;
1023
+ config.build ??= {};
1024
+ config.build.outDir = wxtConfig.outDir;
1025
+ config.build.emptyOutDir = false;
1026
+ config.plugins ??= [];
1027
+ config.plugins.push(
1028
+ download(wxtConfig),
1029
+ devHtmlPrerender(wxtConfig),
1030
+ unimport(wxtConfig),
1031
+ virtualEntrypoint("background", wxtConfig),
1032
+ virtualEntrypoint("content-script", wxtConfig),
1033
+ virtualEntrypoint("unlisted-script", wxtConfig),
1034
+ devServerGlobals(wxtConfig),
1035
+ tsconfigPaths(wxtConfig),
1036
+ noopBackground(),
1037
+ globals(wxtConfig),
1038
+ excludeBrowserPolyfill(wxtConfig)
1039
+ );
1040
+ if (wxtConfig.analysis.enabled) {
1041
+ config.plugins.push(bundleAnalysis());
1042
+ }
1043
+ return config;
1044
+ };
1045
+ const getLibModeConfig = (entrypoint) => {
1046
+ const isVirtual = [
1047
+ "background",
1048
+ "content-script",
1049
+ "unlisted-script"
1050
+ ].includes(entrypoint.type);
1051
+ const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
1052
+ const plugins = [
1053
+ entrypointGroupGlobals(entrypoint)
1054
+ ];
1055
+ if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
1056
+ plugins.push(cssEntrypoints(entrypoint, wxtConfig));
1057
+ }
1058
+ const libMode = {
1059
+ mode: wxtConfig.mode,
1060
+ plugins,
1061
+ build: {
1062
+ lib: {
1063
+ entry,
1064
+ formats: ["iife"],
1065
+ name: "_",
1066
+ fileName: entrypoint.name
1067
+ },
1068
+ rollupOptions: {
1069
+ output: {
1070
+ // There's only a single output for this build, so we use the desired bundle path for the
1071
+ // entry output (like "content-scripts/overlay.js")
1072
+ entryFileNames: getEntrypointBundlePath(
1073
+ entrypoint,
1074
+ wxtConfig.outDir,
1075
+ ".js"
1076
+ ),
1077
+ // Output content script CSS to `content-scripts/`, but all other scripts are written to
1078
+ // `assets/`.
1079
+ assetFileNames: ({ name }) => {
1080
+ if (entrypoint.type === "content-script" && name?.endsWith("css")) {
1081
+ return `content-scripts/${entrypoint.name}.[ext]`;
1082
+ } else {
1083
+ return `assets/${entrypoint.name}.[ext]`;
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ },
1089
+ define: {
1090
+ // See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
1091
+ "process.env.NODE_ENV": JSON.stringify(wxtConfig.mode)
1092
+ }
1093
+ };
1094
+ return libMode;
1095
+ };
1096
+ const getMultiPageConfig = (entrypoints) => {
1097
+ return {
1098
+ mode: wxtConfig.mode,
1099
+ plugins: [
1100
+ multipageMove(entrypoints, wxtConfig),
1101
+ entrypointGroupGlobals(entrypoints)
1102
+ ],
1103
+ build: {
1104
+ rollupOptions: {
1105
+ input: entrypoints.reduce((input, entry) => {
1106
+ input[entry.name] = entry.inputPath;
1107
+ return input;
1108
+ }, {}),
1109
+ output: {
1110
+ // Include a hash to prevent conflicts
1111
+ chunkFileNames: "chunks/[name]-[hash].js",
1112
+ // Include a hash to prevent conflicts
1113
+ entryFileNames: "chunks/[name]-[hash].js",
1114
+ // We can't control the "name", so we need a hash to prevent conflicts
1115
+ assetFileNames: "assets/[name]-[hash].[ext]"
1116
+ }
1117
+ }
1101
1118
  }
1119
+ };
1120
+ };
1121
+ const getCssConfig = (entrypoint) => {
1122
+ return {
1123
+ mode: wxtConfig.mode,
1124
+ plugins: [entrypointGroupGlobals(entrypoint)],
1125
+ build: {
1126
+ rollupOptions: {
1127
+ input: {
1128
+ [entrypoint.name]: entrypoint.inputPath
1129
+ },
1130
+ output: {
1131
+ assetFileNames: () => {
1132
+ if (entrypoint.type === "content-script-style") {
1133
+ return `content-scripts/${entrypoint.name}.[ext]`;
1134
+ } else {
1135
+ return `assets/${entrypoint.name}.[ext]`;
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+ };
1142
+ };
1143
+ return {
1144
+ name: "Vite",
1145
+ version: vite.version,
1146
+ async build(group) {
1147
+ let entryConfig;
1148
+ if (Array.isArray(group))
1149
+ entryConfig = getMultiPageConfig(group);
1150
+ else if (group.inputPath.endsWith(".css"))
1151
+ entryConfig = getCssConfig(group);
1152
+ else
1153
+ entryConfig = getLibModeConfig(group);
1154
+ const buildConfig = vite.mergeConfig(await getBaseConfig(), entryConfig);
1155
+ const result = await vite.build(buildConfig);
1156
+ return {
1157
+ entrypoints: group,
1158
+ chunks: getBuildOutputChunks(result)
1159
+ };
1160
+ },
1161
+ async createServer(info) {
1162
+ const serverConfig = {
1163
+ server: {
1164
+ port: info.port,
1165
+ strictPort: true,
1166
+ host: info.hostname,
1167
+ origin: info.origin
1168
+ }
1169
+ };
1170
+ const baseConfig = await getBaseConfig();
1171
+ const viteServer = await vite.createServer(
1172
+ vite.mergeConfig(baseConfig, serverConfig)
1173
+ );
1174
+ const server = {
1175
+ async listen() {
1176
+ await viteServer.listen(info.port);
1177
+ },
1178
+ transformHtml(...args) {
1179
+ return viteServer.transformIndexHtml(...args);
1180
+ },
1181
+ ws: {
1182
+ send(message, payload) {
1183
+ return viteServer.ws.send(message, payload);
1184
+ },
1185
+ on(message, cb) {
1186
+ viteServer.ws.on(message, cb);
1187
+ }
1188
+ },
1189
+ watcher: viteServer.watcher
1190
+ };
1191
+ return server;
1102
1192
  }
1103
1193
  };
1104
1194
  }
1195
+ function getBuildOutputChunks(result) {
1196
+ if ("on" in result)
1197
+ throw Error("wxt does not support vite watch mode.");
1198
+ if (Array.isArray(result))
1199
+ return result.flatMap(({ output }) => output);
1200
+ return result.output;
1201
+ }
1105
1202
 
1106
1203
  // src/core/utils/building/get-internal-config.ts
1107
- import consola, { LogLevels } from "consola";
1108
- import defu from "defu";
1109
- async function getInternalConfig(inlineConfig, command) {
1204
+ import defu2 from "defu";
1205
+ async function getInternalConfig(inlineConfig, command, server) {
1110
1206
  let userConfig = {};
1111
1207
  let userConfigMetadata;
1112
1208
  if (inlineConfig.configFile !== false) {
@@ -1127,19 +1223,19 @@ async function getInternalConfig(inlineConfig, command) {
1127
1223
  const manifestVersion = mergedConfig.manifestVersion ?? (browser === "firefox" || browser === "safari" ? 2 : 3);
1128
1224
  const mode = mergedConfig.mode ?? (command === "build" ? "production" : "development");
1129
1225
  const env = { browser, command, manifestVersion, mode };
1130
- const root = path5.resolve(
1226
+ const root = path4.resolve(
1131
1227
  inlineConfig.root ?? userConfig.root ?? process.cwd()
1132
1228
  );
1133
- const wxtDir = path5.resolve(root, ".wxt");
1134
- const srcDir = path5.resolve(root, mergedConfig.srcDir ?? root);
1135
- const entrypointsDir = path5.resolve(
1229
+ const wxtDir = path4.resolve(root, ".wxt");
1230
+ const srcDir = path4.resolve(root, mergedConfig.srcDir ?? root);
1231
+ const entrypointsDir = path4.resolve(
1136
1232
  srcDir,
1137
1233
  mergedConfig.entrypointsDir ?? "entrypoints"
1138
1234
  );
1139
- const publicDir = path5.resolve(srcDir, mergedConfig.publicDir ?? "public");
1140
- const typesDir = path5.resolve(wxtDir, "types");
1141
- const outBaseDir = path5.resolve(root, mergedConfig.outDir ?? ".output");
1142
- const outDir = path5.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
1235
+ const publicDir = path4.resolve(srcDir, mergedConfig.publicDir ?? "public");
1236
+ const typesDir = path4.resolve(wxtDir, "types");
1237
+ const outBaseDir = path4.resolve(root, mergedConfig.outDir ?? ".output");
1238
+ const outDir = path4.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
1143
1239
  const runnerConfig = await loadConfig({
1144
1240
  name: "web-ext",
1145
1241
  cwd: root,
@@ -1155,7 +1251,7 @@ async function getInternalConfig(inlineConfig, command) {
1155
1251
  "~": srcDir,
1156
1252
  "@@": root,
1157
1253
  "~~": root
1158
- }).map(([key, value]) => [key, path5.resolve(root, value)])
1254
+ }).map(([key, value]) => [key, path4.resolve(root, value)])
1159
1255
  );
1160
1256
  const finalConfig = {
1161
1257
  browser,
@@ -1176,8 +1272,6 @@ async function getInternalConfig(inlineConfig, command) {
1176
1272
  runnerConfig,
1177
1273
  srcDir,
1178
1274
  typesDir,
1179
- vite: () => ({}),
1180
- // Real value added after this object is initialized.
1181
1275
  wxtDir,
1182
1276
  zip: resolveInternalZipConfig(root, mergedConfig),
1183
1277
  transformManifest(manifest) {
@@ -1192,10 +1286,18 @@ async function getInternalConfig(inlineConfig, command) {
1192
1286
  alias,
1193
1287
  experimental: {
1194
1288
  includeBrowserPolyfill: mergedConfig.experimental?.includeBrowserPolyfill ?? true
1195
- }
1289
+ },
1290
+ server
1291
+ };
1292
+ const builder = await craeteViteBuilder(
1293
+ inlineConfig,
1294
+ userConfig,
1295
+ finalConfig
1296
+ );
1297
+ return {
1298
+ ...finalConfig,
1299
+ builder
1196
1300
  };
1197
- finalConfig.vite = (env2) => resolveInternalViteConfig(env2, mergedConfig, finalConfig);
1198
- return finalConfig;
1199
1301
  }
1200
1302
  async function resolveManifestConfig(env, manifest) {
1201
1303
  return await (typeof manifest === "function" ? manifest(env) : manifest ?? {});
@@ -1207,26 +1309,18 @@ function mergeInlineConfig(inlineConfig, userConfig) {
1207
1309
  } else if (userConfig.imports == null && inlineConfig.imports == null) {
1208
1310
  imports = void 0;
1209
1311
  } else {
1210
- imports = vite2.mergeConfig(
1211
- userConfig.imports ?? {},
1212
- inlineConfig.imports ?? {}
1213
- );
1312
+ imports = defu2(inlineConfig.imports ?? {}, userConfig.imports ?? {});
1214
1313
  }
1215
1314
  const manifest = async (env) => {
1216
1315
  const user = await resolveManifestConfig(env, userConfig.manifest);
1217
1316
  const inline = await resolveManifestConfig(env, inlineConfig.manifest);
1218
- return vite2.mergeConfig(user, inline);
1317
+ return defu2(inline, user);
1219
1318
  };
1220
- const viteConfig = async (env) => {
1221
- const user = await userConfig.vite?.(env);
1222
- const inline = await inlineConfig.vite?.(env);
1223
- return vite2.mergeConfig(user ?? {}, inline ?? {});
1224
- };
1225
- const runner = defu(
1319
+ const runner = defu2(
1226
1320
  inlineConfig.runner ?? {},
1227
1321
  userConfig.runner ?? {}
1228
1322
  );
1229
- const zip = defu(
1323
+ const zip = defu2(
1230
1324
  inlineConfig.zip ?? {},
1231
1325
  userConfig.zip ?? {}
1232
1326
  );
@@ -1245,7 +1339,6 @@ function mergeInlineConfig(inlineConfig, userConfig) {
1245
1339
  runner,
1246
1340
  srcDir: inlineConfig.srcDir ?? userConfig.srcDir,
1247
1341
  outDir: inlineConfig.outDir ?? userConfig.outDir,
1248
- vite: viteConfig,
1249
1342
  zip,
1250
1343
  analysis: {
1251
1344
  enabled: inlineConfig.analysis?.enabled ?? userConfig.analysis?.enabled,
@@ -1259,6 +1352,7 @@ function mergeInlineConfig(inlineConfig, userConfig) {
1259
1352
  ...userConfig.experimental,
1260
1353
  ...inlineConfig.experimental
1261
1354
  },
1355
+ vite: void 0,
1262
1356
  transformManifest: void 0
1263
1357
  };
1264
1358
  }
@@ -1283,38 +1377,6 @@ function resolveInternalZipConfig(root, mergedConfig) {
1283
1377
  ]
1284
1378
  };
1285
1379
  }
1286
- async function resolveInternalViteConfig(env, mergedConfig, finalConfig) {
1287
- const internalVite = await mergedConfig.vite?.(env) ?? {};
1288
- internalVite.root = finalConfig.root;
1289
- internalVite.configFile = false;
1290
- internalVite.logLevel = "warn";
1291
- internalVite.mode = env.mode;
1292
- internalVite.build ??= {};
1293
- internalVite.build.outDir = finalConfig.outDir;
1294
- internalVite.build.emptyOutDir = false;
1295
- internalVite.plugins ??= [];
1296
- internalVite.plugins.push(download(finalConfig));
1297
- internalVite.plugins.push(devHtmlPrerender(finalConfig));
1298
- internalVite.plugins.push(unimport(finalConfig));
1299
- internalVite.plugins.push(
1300
- virtualEntrypoint("background", finalConfig)
1301
- );
1302
- internalVite.plugins.push(
1303
- virtualEntrypoint("content-script", finalConfig)
1304
- );
1305
- internalVite.plugins.push(
1306
- virtualEntrypoint("unlisted-script", finalConfig)
1307
- );
1308
- internalVite.plugins.push(devServerGlobals(finalConfig));
1309
- internalVite.plugins.push(tsconfigPaths(finalConfig));
1310
- internalVite.plugins.push(noopBackground());
1311
- if (finalConfig.analysis.enabled) {
1312
- internalVite.plugins.push(bundleAnalysis());
1313
- }
1314
- internalVite.plugins.push(globals(finalConfig));
1315
- internalVite.plugins.push(excludeBrowserPolyfill(finalConfig));
1316
- return internalVite;
1317
- }
1318
1380
 
1319
1381
  // src/core/utils/building/group-entrypoints.ts
1320
1382
  function groupEntrypoints(entrypoints) {
@@ -1378,16 +1440,16 @@ ${noImports}`;
1378
1440
 
1379
1441
  // src/core/utils/building/import-entrypoint.ts
1380
1442
  import { transformSync } from "esbuild";
1381
- async function importEntrypointFile(path7, config) {
1382
- config.logger.debug("Loading file metadata:", path7);
1383
- const normalPath = normalizePath(path7);
1443
+ async function importEntrypointFile(path6, config) {
1444
+ config.logger.debug("Loading file metadata:", path6);
1445
+ const normalPath = normalizePath(path6);
1384
1446
  const unimport2 = createUnimport3({
1385
1447
  ...getUnimportOptions(config),
1386
1448
  // Only allow specific imports, not all from the project
1387
1449
  dirs: []
1388
1450
  });
1389
1451
  await unimport2.init();
1390
- const text = await fs7.readFile(path7, "utf-8");
1452
+ const text = await fs7.readFile(path6, "utf-8");
1391
1453
  const textNoImports = removeProjectImportStatements(text);
1392
1454
  const { code } = await unimport2.injectImports(textNoImports);
1393
1455
  config.logger.debug(
@@ -1418,7 +1480,7 @@ async function importEntrypointFile(path7, config) {
1418
1480
  }
1419
1481
  });
1420
1482
  try {
1421
- const res = await jiti(path7);
1483
+ const res = await jiti(path6);
1422
1484
  return res.default;
1423
1485
  } catch (err) {
1424
1486
  config.logger.error(err);
@@ -1436,14 +1498,13 @@ function getEsbuildOptions(opts) {
1436
1498
 
1437
1499
  // src/core/utils/building/internal-build.ts
1438
1500
  import pc4 from "picocolors";
1439
- import * as vite3 from "vite";
1440
1501
  import fs11 from "fs-extra";
1441
1502
 
1442
1503
  // src/core/utils/log/printBuildSummary.ts
1443
1504
  import { resolve as resolve9 } from "path";
1444
1505
 
1445
1506
  // src/core/utils/log/printFileList.ts
1446
- import path6 from "node:path";
1507
+ import path5 from "node:path";
1447
1508
  import pc2 from "picocolors";
1448
1509
  import fs8 from "fs-extra";
1449
1510
  import { filesize } from "filesize";
@@ -1481,8 +1542,8 @@ async function printFileList(log, header, baseDir, files) {
1481
1542
  const fileRows = await Promise.all(
1482
1543
  files.map(async (file, i) => {
1483
1544
  const parts = [
1484
- path6.relative(process.cwd(), baseDir) + path6.sep,
1485
- path6.relative(baseDir, file)
1545
+ path5.relative(process.cwd(), baseDir) + path5.sep,
1546
+ path5.relative(baseDir, file)
1486
1547
  ];
1487
1548
  const prefix = i === files.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
1488
1549
  const color = getChunkColor(file);
@@ -1673,37 +1734,34 @@ async function getPackageJson(config) {
1673
1734
 
1674
1735
  // src/core/utils/manifest.ts
1675
1736
  import { produce } from "immer";
1676
- import defu2 from "defu";
1737
+ import defu3 from "defu";
1677
1738
  async function writeManifest(manifest, output, config) {
1678
1739
  const str = config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
1679
1740
  await fs10.ensureDir(config.outDir);
1680
1741
  await writeFileIfDifferent(resolve11(config.outDir, "manifest.json"), str);
1681
1742
  output.publicAssets.unshift({
1682
1743
  type: "asset",
1683
- fileName: "manifest.json",
1684
- name: "manifest",
1685
- needsCodeReference: false,
1686
- source: str
1744
+ fileName: "manifest.json"
1687
1745
  });
1688
1746
  }
1689
1747
  async function generateMainfest(entrypoints, buildOutput, config) {
1690
1748
  const pkg = await getPackageJson(config);
1691
1749
  const versionName = config.manifest.version_name ?? pkg?.version;
1692
- const version3 = config.manifest.version ?? simplifyVersion(pkg?.version);
1750
+ const version2 = config.manifest.version ?? simplifyVersion(pkg?.version);
1693
1751
  const baseManifest = {
1694
1752
  manifest_version: config.manifestVersion,
1695
1753
  name: pkg?.name,
1696
1754
  description: pkg?.description,
1697
- version: version3,
1755
+ version: version2,
1698
1756
  version_name: (
1699
1757
  // Firefox doesn't support version_name
1700
- config.browser === "firefox" || versionName === version3 ? void 0 : versionName
1758
+ config.browser === "firefox" || versionName === version2 ? void 0 : versionName
1701
1759
  ),
1702
1760
  short_name: pkg?.shortName,
1703
1761
  icons: discoverIcons(buildOutput)
1704
1762
  };
1705
1763
  const userManifest = config.manifest;
1706
- const manifest = defu2(
1764
+ const manifest = defu3(
1707
1765
  userManifest,
1708
1766
  baseManifest
1709
1767
  );
@@ -1725,14 +1783,14 @@ async function generateMainfest(entrypoints, buildOutput, config) {
1725
1783
  return finalManifest;
1726
1784
  }
1727
1785
  function simplifyVersion(versionName) {
1728
- const version3 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
1786
+ const version2 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
1729
1787
  versionName
1730
1788
  )?.[1];
1731
- if (version3 == null)
1789
+ if (version2 == null)
1732
1790
  throw Error(
1733
1791
  `Cannot simplify package.json version "${versionName}" to a valid extension version, "X.Y.Z"`
1734
1792
  );
1735
- return version3;
1793
+ return version2;
1736
1794
  }
1737
1795
  function addEntrypoints(manifest, entrypoints, buildOutput, config) {
1738
1796
  const entriesByType = entrypoints.reduce((map, entrypoint) => {
@@ -2104,7 +2162,7 @@ async function internalBuild(config) {
2104
2162
  const target = `${config.browser}-mv${config.manifestVersion}`;
2105
2163
  config.logger.info(
2106
2164
  `${verb} ${pc4.cyan(target)} for ${pc4.cyan(config.mode)} with ${pc4.green(
2107
- `Vite ${vite3.version}`
2165
+ `${config.builder.name} ${config.builder.version}`
2108
2166
  )}`
2109
2167
  );
2110
2168
  const startTime = Date.now();
@@ -2130,7 +2188,7 @@ async function internalBuild(config) {
2130
2188
  return output;
2131
2189
  }
2132
2190
  async function combineAnalysisStats(config) {
2133
- const { execaCommand } = await import("./execa-Z7B33P3C.js");
2191
+ const { execaCommand } = await import("./execa-4F7CCWCA.js");
2134
2192
  const unixFiles = await glob2(`stats-*.json`, {
2135
2193
  cwd: config.outDir,
2136
2194
  absolute: true
@@ -2153,7 +2211,7 @@ async function findEntrypoints(config) {
2153
2211
  let hasBackground = false;
2154
2212
  const possibleEntrypoints = await Promise.all(
2155
2213
  relativePaths.map(async (relativePath) => {
2156
- const path7 = resolve12(config.entrypointsDir, relativePath);
2214
+ const path6 = resolve12(config.entrypointsDir, relativePath);
2157
2215
  const matchingGlob = pathGlobs.find(
2158
2216
  (glob4) => minimatch(relativePath, glob4)
2159
2217
  );
@@ -2173,23 +2231,23 @@ ${JSON.stringify(
2173
2231
  return;
2174
2232
  switch (type) {
2175
2233
  case "popup":
2176
- return await getPopupEntrypoint(config, path7);
2234
+ return await getPopupEntrypoint(config, path6);
2177
2235
  case "options":
2178
- return await getOptionsEntrypoint(config, path7);
2236
+ return await getOptionsEntrypoint(config, path6);
2179
2237
  case "background":
2180
2238
  hasBackground = true;
2181
- return await getBackgroundEntrypoint(config, path7);
2239
+ return await getBackgroundEntrypoint(config, path6);
2182
2240
  case "content-script":
2183
- return await getContentScriptEntrypoint(config, path7);
2241
+ return await getContentScriptEntrypoint(config, path6);
2184
2242
  case "unlisted-page":
2185
- return await getUnlistedPageEntrypoint(config, path7);
2243
+ return await getUnlistedPageEntrypoint(config, path6);
2186
2244
  case "unlisted-script":
2187
- return await getUnlistedScriptEntrypoint(config, path7);
2245
+ return await getUnlistedScriptEntrypoint(config, path6);
2188
2246
  case "content-script-style":
2189
2247
  return {
2190
2248
  type,
2191
- name: getEntrypointName(config.entrypointsDir, path7),
2192
- inputPath: path7,
2249
+ name: getEntrypointName(config.entrypointsDir, path6),
2250
+ inputPath: path6,
2193
2251
  outputDir: resolve12(config.outDir, CONTENT_SCRIPT_OUT_DIR),
2194
2252
  options: {
2195
2253
  include: void 0,
@@ -2199,8 +2257,8 @@ ${JSON.stringify(
2199
2257
  default:
2200
2258
  return {
2201
2259
  type,
2202
- name: getEntrypointName(config.entrypointsDir, path7),
2203
- inputPath: path7,
2260
+ name: getEntrypointName(config.entrypointsDir, path6),
2261
+ inputPath: path6,
2204
2262
  outputDir: config.outDir,
2205
2263
  options: {
2206
2264
  include: void 0,
@@ -2263,8 +2321,8 @@ function getHtmlBaseOptions(document) {
2263
2321
  }
2264
2322
  return options;
2265
2323
  }
2266
- async function getPopupEntrypoint(config, path7) {
2267
- const content = await fs12.readFile(path7, "utf-8");
2324
+ async function getPopupEntrypoint(config, path6) {
2325
+ const content = await fs12.readFile(path6, "utf-8");
2268
2326
  const { document } = parseHTML2(content);
2269
2327
  const options = getHtmlBaseOptions(document);
2270
2328
  const title = document.querySelector("title");
@@ -2293,12 +2351,12 @@ async function getPopupEntrypoint(config, path7) {
2293
2351
  type: "popup",
2294
2352
  name: "popup",
2295
2353
  options,
2296
- inputPath: path7,
2354
+ inputPath: path6,
2297
2355
  outputDir: config.outDir
2298
2356
  };
2299
2357
  }
2300
- async function getOptionsEntrypoint(config, path7) {
2301
- const content = await fs12.readFile(path7, "utf-8");
2358
+ async function getOptionsEntrypoint(config, path6) {
2359
+ const content = await fs12.readFile(path6, "utf-8");
2302
2360
  const { document } = parseHTML2(content);
2303
2361
  const options = getHtmlBaseOptions(document);
2304
2362
  const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
@@ -2317,25 +2375,25 @@ async function getOptionsEntrypoint(config, path7) {
2317
2375
  type: "options",
2318
2376
  name: "options",
2319
2377
  options,
2320
- inputPath: path7,
2378
+ inputPath: path6,
2321
2379
  outputDir: config.outDir
2322
2380
  };
2323
2381
  }
2324
- async function getUnlistedPageEntrypoint(config, path7) {
2325
- const content = await fs12.readFile(path7, "utf-8");
2382
+ async function getUnlistedPageEntrypoint(config, path6) {
2383
+ const content = await fs12.readFile(path6, "utf-8");
2326
2384
  const { document } = parseHTML2(content);
2327
2385
  return {
2328
2386
  type: "unlisted-page",
2329
- name: getEntrypointName(config.entrypointsDir, path7),
2330
- inputPath: path7,
2387
+ name: getEntrypointName(config.entrypointsDir, path6),
2388
+ inputPath: path6,
2331
2389
  outputDir: config.outDir,
2332
2390
  options: getHtmlBaseOptions(document)
2333
2391
  };
2334
2392
  }
2335
- async function getUnlistedScriptEntrypoint(config, path7) {
2336
- const name = getEntrypointName(config.entrypointsDir, path7);
2393
+ async function getUnlistedScriptEntrypoint(config, path6) {
2394
+ const name = getEntrypointName(config.entrypointsDir, path6);
2337
2395
  const defaultExport = await importEntrypointFile(
2338
- path7,
2396
+ path6,
2339
2397
  config
2340
2398
  );
2341
2399
  if (defaultExport == null) {
@@ -2348,17 +2406,17 @@ async function getUnlistedScriptEntrypoint(config, path7) {
2348
2406
  return {
2349
2407
  type: "unlisted-script",
2350
2408
  name,
2351
- inputPath: path7,
2409
+ inputPath: path6,
2352
2410
  outputDir: config.outDir,
2353
2411
  options
2354
2412
  };
2355
2413
  }
2356
- async function getBackgroundEntrypoint(config, path7) {
2414
+ async function getBackgroundEntrypoint(config, path6) {
2357
2415
  const name = "background";
2358
2416
  let options = {};
2359
- if (path7 !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
2417
+ if (path6 !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
2360
2418
  const defaultExport = await importEntrypointFile(
2361
- path7,
2419
+ path6,
2362
2420
  config
2363
2421
  );
2364
2422
  if (defaultExport == null) {
@@ -2372,7 +2430,7 @@ async function getBackgroundEntrypoint(config, path7) {
2372
2430
  return {
2373
2431
  type: "background",
2374
2432
  name,
2375
- inputPath: path7,
2433
+ inputPath: path6,
2376
2434
  outputDir: config.outDir,
2377
2435
  options: {
2378
2436
  ...options,
@@ -2381,9 +2439,9 @@ async function getBackgroundEntrypoint(config, path7) {
2381
2439
  }
2382
2440
  };
2383
2441
  }
2384
- async function getContentScriptEntrypoint(config, path7) {
2385
- const name = getEntrypointName(config.entrypointsDir, path7);
2386
- const { main: _, ...options } = await importEntrypointFile(path7, config);
2442
+ async function getContentScriptEntrypoint(config, path6) {
2443
+ const name = getEntrypointName(config.entrypointsDir, path6);
2444
+ const { main: _, ...options } = await importEntrypointFile(path6, config);
2387
2445
  if (options == null) {
2388
2446
  throw Error(
2389
2447
  `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
@@ -2392,7 +2450,7 @@ async function getContentScriptEntrypoint(config, path7) {
2392
2450
  return {
2393
2451
  type: "content-script",
2394
2452
  name,
2395
- inputPath: path7,
2453
+ inputPath: path6,
2396
2454
  outputDir: resolve12(config.outDir, CONTENT_SCRIPT_OUT_DIR),
2397
2455
  options
2398
2456
  };
@@ -2441,9 +2499,12 @@ var PATH_GLOB_TO_TYPE_MAP = {
2441
2499
  var CONTENT_SCRIPT_OUT_DIR = "content-scripts";
2442
2500
 
2443
2501
  export {
2502
+ detectDevChanges,
2444
2503
  getEntrypointOutputFile,
2445
2504
  getEntrypointBundlePath,
2446
2505
  resolvePerBrowserOption,
2506
+ findEntrypoints,
2507
+ generateTypesDir,
2447
2508
  formatDuration,
2448
2509
  download,
2449
2510
  unimport,
@@ -2451,9 +2512,6 @@ export {
2451
2512
  globals,
2452
2513
  webextensionPolyfillAlias,
2453
2514
  webextensionPolyfillInlineDeps,
2454
- detectDevChanges,
2455
- findEntrypoints,
2456
- generateTypesDir,
2457
2515
  getInternalConfig,
2458
2516
  kebabCaseAlphanumeric,
2459
2517
  printFileList,