wxt 0.18.14 → 0.19.0-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/README.md +1 -1
  2. package/bin/wxt.mjs +1 -1
  3. package/dist/browser/chrome.d.ts +17 -0
  4. package/dist/browser/chrome.mjs +7 -0
  5. package/dist/browser/index.d.ts +4 -0
  6. package/dist/browser/index.mjs +1 -0
  7. package/dist/browser/webextension-polyfill.d.ts +18 -0
  8. package/dist/browser/webextension-polyfill.mjs +2 -0
  9. package/dist/builtin-modules/index.d.ts +2 -0
  10. package/dist/builtin-modules/index.mjs +2 -0
  11. package/dist/builtin-modules/unimport.d.ts +8 -0
  12. package/dist/builtin-modules/unimport.mjs +99 -0
  13. package/dist/cli/cli-utils.d.ts +25 -0
  14. package/dist/cli/cli-utils.mjs +58 -0
  15. package/dist/cli/commands.d.ts +2 -0
  16. package/dist/cli/commands.mjs +104 -0
  17. package/dist/cli/index.d.ts +1 -0
  18. package/dist/cli/index.mjs +11 -0
  19. package/dist/client/app-config.d.ts +2 -0
  20. package/dist/client/app-config.mjs +4 -0
  21. package/dist/client/content-scripts/content-script-context.d.ts +114 -0
  22. package/dist/client/content-scripts/content-script-context.mjs +169 -0
  23. package/dist/client/content-scripts/custom-events.d.ts +10 -0
  24. package/dist/client/content-scripts/custom-events.mjs +13 -0
  25. package/dist/client/content-scripts/index.d.ts +2 -0
  26. package/dist/client/content-scripts/index.mjs +2 -0
  27. package/dist/client/content-scripts/location-watcher.d.ts +12 -0
  28. package/dist/client/content-scripts/location-watcher.mjs +22 -0
  29. package/dist/client/content-scripts/ui/index.d.ts +23 -0
  30. package/dist/client/content-scripts/ui/index.mjs +188 -0
  31. package/dist/{client.d.ts → client/content-scripts/ui/types.d.ts} +15 -43
  32. package/dist/client/content-scripts/ui/types.mjs +0 -0
  33. package/dist/client/index.d.ts +7 -0
  34. package/dist/client/index.mjs +2 -0
  35. package/dist/core/build.d.ts +15 -0
  36. package/dist/core/build.mjs +6 -0
  37. package/dist/core/builders/vite/index.d.ts +3 -0
  38. package/dist/core/builders/vite/index.mjs +297 -0
  39. package/dist/core/builders/vite/plugins/bundleAnalysis.d.ts +7 -0
  40. package/dist/core/builders/vite/plugins/bundleAnalysis.mjs +15 -0
  41. package/dist/core/builders/vite/plugins/cssEntrypoints.d.ts +13 -0
  42. package/dist/core/builders/vite/plugins/cssEntrypoints.mjs +22 -0
  43. package/dist/core/builders/vite/plugins/defineImportMeta.d.ts +14 -0
  44. package/dist/core/builders/vite/plugins/defineImportMeta.mjs +13 -0
  45. package/dist/core/builders/vite/plugins/devHtmlPrerender.d.ts +7 -0
  46. package/dist/core/builders/vite/plugins/devHtmlPrerender.mjs +140 -0
  47. package/dist/core/builders/vite/plugins/devServerGlobals.d.ts +6 -0
  48. package/dist/core/builders/vite/plugins/devServerGlobals.mjs +15 -0
  49. package/dist/core/builders/vite/plugins/download.d.ts +10 -0
  50. package/dist/core/builders/vite/plugins/download.mjs +14 -0
  51. package/dist/core/builders/vite/plugins/entrypointGroupGlobals.d.ts +6 -0
  52. package/dist/core/builders/vite/plugins/entrypointGroupGlobals.mjs +16 -0
  53. package/dist/core/builders/vite/plugins/extensionApiMock.d.ts +11 -0
  54. package/dist/core/builders/vite/plugins/extensionApiMock.mjs +26 -0
  55. package/dist/core/builders/vite/plugins/globals.d.ts +3 -0
  56. package/dist/core/builders/vite/plugins/globals.mjs +15 -0
  57. package/dist/core/builders/vite/plugins/index.d.ts +17 -0
  58. package/dist/core/builders/vite/plugins/index.mjs +17 -0
  59. package/dist/core/builders/vite/plugins/multipageMove.d.ts +20 -0
  60. package/dist/core/builders/vite/plugins/multipageMove.mjs +59 -0
  61. package/dist/core/builders/vite/plugins/noopBackground.d.ts +6 -0
  62. package/dist/core/builders/vite/plugins/noopBackground.mjs +17 -0
  63. package/dist/core/builders/vite/plugins/removeEntrypointMainFunction.d.ts +6 -0
  64. package/dist/core/builders/vite/plugins/removeEntrypointMainFunction.mjs +12 -0
  65. package/dist/core/builders/vite/plugins/resolveAppConfig.d.ts +6 -0
  66. package/dist/core/builders/vite/plugins/resolveAppConfig.mjs +26 -0
  67. package/dist/core/builders/vite/plugins/resolveExtensionApi.d.ts +10 -0
  68. package/dist/core/builders/vite/plugins/resolveExtensionApi.mjs +15 -0
  69. package/dist/core/builders/vite/plugins/resolveVirtualModules.d.ts +6 -0
  70. package/dist/core/builders/vite/plugins/resolveVirtualModules.mjs +30 -0
  71. package/dist/core/builders/vite/plugins/tsconfigPaths.d.ts +3 -0
  72. package/dist/core/builders/vite/plugins/tsconfigPaths.mjs +12 -0
  73. package/dist/core/builders/vite/plugins/wxtPluginLoader.d.ts +6 -0
  74. package/dist/core/builders/vite/plugins/wxtPluginLoader.mjs +56 -0
  75. package/dist/core/clean.d.ts +21 -0
  76. package/dist/core/clean.mjs +38 -0
  77. package/dist/core/create-server.d.ts +11 -0
  78. package/dist/core/create-server.mjs +206 -0
  79. package/dist/core/define-config.d.ts +2 -0
  80. package/dist/core/define-config.mjs +3 -0
  81. package/dist/core/define-runner-config.d.ts +2 -0
  82. package/dist/core/define-runner-config.mjs +3 -0
  83. package/dist/core/index.d.ts +8 -0
  84. package/dist/core/index.mjs +8 -0
  85. package/dist/core/initialize.d.ts +5 -0
  86. package/dist/core/initialize.mjs +128 -0
  87. package/dist/core/package-managers/bun.d.ts +2 -0
  88. package/dist/core/package-managers/bun.mjs +19 -0
  89. package/dist/core/package-managers/index.d.ts +2 -0
  90. package/dist/core/package-managers/index.mjs +65 -0
  91. package/dist/core/package-managers/npm.d.ts +17 -0
  92. package/dist/core/package-managers/npm.mjs +58 -0
  93. package/dist/core/package-managers/pnpm.d.ts +2 -0
  94. package/dist/core/package-managers/pnpm.mjs +21 -0
  95. package/dist/core/package-managers/types.d.ts +2 -0
  96. package/dist/core/package-managers/types.mjs +0 -0
  97. package/dist/core/package-managers/yarn.d.ts +2 -0
  98. package/dist/core/package-managers/yarn.mjs +31 -0
  99. package/dist/core/prepare.d.ts +2 -0
  100. package/dist/core/prepare.mjs +8 -0
  101. package/dist/core/runners/index.d.ts +2 -0
  102. package/dist/core/runners/index.mjs +12 -0
  103. package/dist/core/runners/manual.d.ts +5 -0
  104. package/dist/core/runners/manual.mjs +16 -0
  105. package/dist/core/runners/safari.d.ts +5 -0
  106. package/dist/core/runners/safari.mjs +16 -0
  107. package/dist/core/runners/web-ext.d.ts +5 -0
  108. package/dist/core/runners/web-ext.mjs +78 -0
  109. package/dist/core/runners/wsl.d.ts +5 -0
  110. package/dist/core/runners/wsl.mjs +16 -0
  111. package/dist/core/utils/arrays.d.ts +13 -0
  112. package/dist/{chunk-BERPNPEZ.js → core/utils/arrays.mjs} +6 -10
  113. package/dist/core/utils/building/build-entrypoints.d.ts +3 -0
  114. package/dist/core/utils/building/build-entrypoints.mjs +47 -0
  115. package/dist/core/utils/building/detect-dev-changes.d.ts +57 -0
  116. package/dist/core/utils/building/detect-dev-changes.mjs +93 -0
  117. package/dist/core/utils/building/find-entrypoints.d.ts +5 -0
  118. package/dist/core/utils/building/find-entrypoints.mjs +385 -0
  119. package/dist/core/utils/building/generate-wxt-dir.d.ts +5 -0
  120. package/dist/core/utils/building/generate-wxt-dir.mjs +190 -0
  121. package/dist/core/utils/building/group-entrypoints.d.ts +8 -0
  122. package/dist/core/utils/building/group-entrypoints.mjs +37 -0
  123. package/dist/core/utils/building/import-entrypoint.d.ts +16 -0
  124. package/dist/core/utils/building/import-entrypoint.mjs +97 -0
  125. package/dist/core/utils/building/index.d.ts +9 -0
  126. package/dist/core/utils/building/index.mjs +9 -0
  127. package/dist/core/utils/building/internal-build.d.ts +12 -0
  128. package/dist/core/utils/building/internal-build.mjs +112 -0
  129. package/dist/core/utils/building/rebuild.d.ts +23 -0
  130. package/dist/core/utils/building/rebuild.mjs +39 -0
  131. package/dist/core/utils/building/resolve-config.d.ts +11 -0
  132. package/dist/core/utils/building/resolve-config.mjs +365 -0
  133. package/dist/core/utils/cache.d.ts +8 -0
  134. package/dist/core/utils/cache.mjs +21 -0
  135. package/dist/core/utils/cli.d.ts +3 -0
  136. package/dist/core/utils/cli.mjs +26 -0
  137. package/dist/core/utils/constants.d.ts +5 -0
  138. package/dist/core/utils/constants.mjs +1 -0
  139. package/dist/core/utils/content-scripts.d.ts +11 -0
  140. package/dist/core/utils/content-scripts.mjs +60 -0
  141. package/dist/core/utils/content-security-policy.d.ts +14 -0
  142. package/dist/core/utils/content-security-policy.mjs +39 -0
  143. package/dist/core/utils/entrypoints.d.ts +25 -0
  144. package/dist/core/utils/entrypoints.mjs +31 -0
  145. package/dist/core/utils/eslint.d.ts +1 -0
  146. package/dist/core/utils/eslint.mjs +11 -0
  147. package/dist/core/utils/fs.d.ts +13 -0
  148. package/dist/core/utils/fs.mjs +15 -0
  149. package/dist/core/utils/globals.d.ts +11 -0
  150. package/dist/core/utils/globals.mjs +53 -0
  151. package/dist/core/utils/i18n.d.ts +11 -0
  152. package/dist/core/utils/i18n.mjs +35 -0
  153. package/dist/core/utils/log/index.d.ts +4 -0
  154. package/dist/core/utils/log/index.mjs +4 -0
  155. package/dist/core/utils/log/printBuildSummary.d.ts +2 -0
  156. package/dist/core/utils/log/printBuildSummary.mjs +32 -0
  157. package/dist/core/utils/log/printFileList.d.ts +1 -0
  158. package/dist/core/utils/log/printFileList.mjs +42 -0
  159. package/dist/core/utils/log/printHeader.d.ts +1 -0
  160. package/dist/core/utils/log/printHeader.mjs +7 -0
  161. package/dist/core/utils/log/printTable.d.ts +1 -0
  162. package/dist/core/utils/log/printTable.mjs +22 -0
  163. package/dist/core/utils/manifest.d.ts +44 -0
  164. package/dist/core/utils/manifest.mjs +512 -0
  165. package/dist/core/utils/network.d.ts +7 -0
  166. package/dist/core/utils/network.mjs +38 -0
  167. package/dist/core/utils/package.d.ts +6 -0
  168. package/dist/core/utils/package.mjs +14 -0
  169. package/dist/core/utils/paths.d.ts +11 -0
  170. package/dist/core/utils/paths.mjs +10 -0
  171. package/dist/core/utils/strings.d.ts +14 -0
  172. package/dist/core/utils/strings.mjs +18 -0
  173. package/dist/core/utils/testing/fake-objects.d.ts +4520 -0
  174. package/dist/core/utils/testing/fake-objects.mjs +323 -0
  175. package/dist/core/utils/time.d.ts +9 -0
  176. package/dist/core/utils/time.mjs +17 -0
  177. package/dist/core/utils/transform.d.ts +9 -0
  178. package/dist/core/utils/transform.mjs +17 -0
  179. package/dist/core/utils/types.d.ts +10 -0
  180. package/dist/core/utils/types.mjs +0 -0
  181. package/dist/core/utils/validation.d.ts +15 -0
  182. package/dist/core/utils/validation.mjs +55 -0
  183. package/dist/core/utils/virtual-modules.d.ts +22 -0
  184. package/dist/core/utils/virtual-modules.mjs +14 -0
  185. package/dist/core/utils/wsl.d.ts +4 -0
  186. package/dist/core/utils/wsl.mjs +4 -0
  187. package/dist/core/wxt.d.ts +19 -0
  188. package/dist/core/wxt.mjs +41 -0
  189. package/dist/core/zip.d.ts +7 -0
  190. package/dist/core/zip.mjs +137 -0
  191. package/dist/index.d.ts +4 -79
  192. package/dist/index.mjs +3 -0
  193. package/dist/modules.d.ts +10 -20
  194. package/dist/{chunk-6XSIWUWF.js → modules.mjs} +7 -16
  195. package/dist/sandbox/define-app-config.d.ts +19 -0
  196. package/dist/sandbox/define-app-config.mjs +3 -0
  197. package/dist/sandbox/define-background.d.ts +3 -0
  198. package/dist/sandbox/define-background.mjs +4 -0
  199. package/dist/sandbox/define-content-script.d.ts +2 -0
  200. package/dist/sandbox/define-content-script.mjs +3 -0
  201. package/dist/sandbox/define-unlisted-script.d.ts +3 -0
  202. package/dist/sandbox/define-unlisted-script.mjs +4 -0
  203. package/dist/sandbox/define-wxt-plugin.d.ts +2 -0
  204. package/dist/sandbox/define-wxt-plugin.mjs +3 -0
  205. package/dist/sandbox/dev-server-websocket.d.ts +21 -0
  206. package/dist/sandbox/dev-server-websocket.mjs +37 -0
  207. package/dist/sandbox/index.d.ts +11 -0
  208. package/dist/sandbox/index.mjs +6 -0
  209. package/dist/sandbox/utils/logger.d.ts +9 -0
  210. package/dist/sandbox/utils/logger.mjs +15 -0
  211. package/dist/storage.d.ts +13 -15
  212. package/dist/{storage.js → storage.mjs} +5 -34
  213. package/dist/testing/fake-browser.d.ts +1 -0
  214. package/dist/testing/fake-browser.mjs +1 -0
  215. package/dist/testing/index.d.ts +5 -0
  216. package/dist/testing/index.mjs +2 -0
  217. package/dist/{testing.d.ts → testing/wxt-vitest-plugin.d.ts} +3 -15
  218. package/dist/testing/wxt-vitest-plugin.mjs +26 -0
  219. package/dist/{index-CM35KuSH.d.cts → types.d.ts} +119 -250
  220. package/dist/types.mjs +0 -0
  221. package/dist/version.d.ts +1 -0
  222. package/dist/version.mjs +1 -0
  223. package/dist/virtual/{background-entrypoint.js → background-entrypoint.mjs} +31 -40
  224. package/dist/virtual/{content-script-isolated-world-entrypoint.js → content-script-isolated-world-entrypoint.mjs} +9 -13
  225. package/dist/virtual/{content-script-main-world-entrypoint.js → content-script-main-world-entrypoint.mjs} +9 -14
  226. package/dist/virtual/mock-browser.mjs +6 -0
  227. package/dist/virtual/{reload-html.js → reload-html.mjs} +8 -9
  228. package/dist/virtual/{unlisted-script-entrypoint.js → unlisted-script-entrypoint.mjs} +8 -12
  229. package/package.json +51 -57
  230. package/dist/browser.d.ts +0 -18
  231. package/dist/browser.js +0 -6
  232. package/dist/chunk-BM6QYGAW.js +0 -1063
  233. package/dist/chunk-FNTE2L27.js +0 -7
  234. package/dist/chunk-KPD5J7PZ.js +0 -1065
  235. package/dist/chunk-QGM4M3NI.js +0 -37
  236. package/dist/chunk-SGKCDMVR.js +0 -38
  237. package/dist/chunk-ZONHOHIX.js +0 -3608
  238. package/dist/cli.d.ts +0 -2
  239. package/dist/cli.js +0 -4429
  240. package/dist/client.js +0 -424
  241. package/dist/define-app-config-bg54F_lV.d.ts +0 -294
  242. package/dist/execa-4UBDUBJZ.js +0 -7244
  243. package/dist/execa-QLUM2B3W.js +0 -7245
  244. package/dist/index-CM35KuSH.d.ts +0 -1401
  245. package/dist/index.cjs +0 -14464
  246. package/dist/index.d.cts +0 -81
  247. package/dist/index.js +0 -696
  248. package/dist/modules.cjs +0 -96
  249. package/dist/modules.d.cts +0 -119
  250. package/dist/modules.js +0 -17
  251. package/dist/prompt-25QIVJDC.js +0 -755
  252. package/dist/prompt-7BMKNSWS.js +0 -754
  253. package/dist/sandbox.d.ts +0 -16
  254. package/dist/sandbox.js +0 -36
  255. package/dist/storage.cjs +0 -439
  256. package/dist/storage.d.cts +0 -200
  257. package/dist/testing.cjs +0 -2806
  258. package/dist/testing.d.cts +0 -30
  259. package/dist/testing.js +0 -40
  260. package/dist/virtual/mock-browser.js +0 -6
package/dist/cli.js DELETED
@@ -1,4429 +0,0 @@
1
- import { createRequire } from 'module';const require = createRequire(import.meta.url);
2
- import {
3
- LogLevels,
4
- consola
5
- } from "./chunk-KPD5J7PZ.js";
6
- import {
7
- __require
8
- } from "./chunk-SGKCDMVR.js";
9
-
10
- // src/cli/commands.ts
11
- import cac from "cac";
12
-
13
- // src/core/utils/fs.ts
14
- import fs3 from "fs-extra";
15
- import glob2 from "fast-glob";
16
-
17
- // src/core/utils/paths.ts
18
- import systemPath from "node:path";
19
- import normalize from "normalize-path";
20
- function normalizePath(path11) {
21
- return normalize(path11);
22
- }
23
- function unnormalizePath(path11) {
24
- return systemPath.normalize(path11);
25
- }
26
- var CSS_EXTENSIONS = ["css", "scss", "sass", "less", "styl", "stylus"];
27
- var CSS_EXTENSIONS_PATTERN = `+(${CSS_EXTENSIONS.join("|")})`;
28
-
29
- // src/core/wxt.ts
30
- import { createHooks } from "hookable";
31
-
32
- // src/core/package-managers/index.ts
33
- import {
34
- detectPackageManager,
35
- addDependency,
36
- addDevDependency,
37
- ensureDependencyInstalled,
38
- installDependencies,
39
- removeDependency
40
- } from "nypm";
41
-
42
- // src/core/package-managers/npm.ts
43
- import path from "node:path";
44
- import { ensureDir } from "fs-extra";
45
- var npm = {
46
- overridesKey: "overrides",
47
- async downloadDependency(id, downloadDir) {
48
- await ensureDir(downloadDir);
49
- const { execa } = await import("./execa-QLUM2B3W.js");
50
- const res = await execa("npm", ["pack", id, "--json"], {
51
- cwd: downloadDir
52
- });
53
- const packed = JSON.parse(res.stdout);
54
- return path.resolve(downloadDir, packed[0].filename);
55
- },
56
- async listDependencies(options) {
57
- const args = ["ls", "--json"];
58
- if (options?.all) {
59
- args.push("--depth", "Infinity");
60
- }
61
- const { execa } = await import("./execa-QLUM2B3W.js");
62
- const res = await execa("npm", args, { cwd: options?.cwd });
63
- const project = JSON.parse(res.stdout);
64
- return flattenNpmListOutput([project]);
65
- }
66
- };
67
- function flattenNpmListOutput(projects) {
68
- const queue = projects.flatMap(
69
- (project) => {
70
- const acc = [];
71
- if (project.dependencies) acc.push(project.dependencies);
72
- if (project.devDependencies) acc.push(project.devDependencies);
73
- return acc;
74
- }
75
- );
76
- const dependencies = [];
77
- while (queue.length > 0) {
78
- Object.entries(queue.pop()).forEach(([name, meta]) => {
79
- dependencies.push({
80
- name,
81
- version: meta.version
82
- });
83
- if (meta.dependencies) queue.push(meta.dependencies);
84
- if (meta.devDependencies) queue.push(meta.devDependencies);
85
- });
86
- }
87
- return dedupeDependencies(dependencies);
88
- }
89
- function dedupeDependencies(dependencies) {
90
- const hashes = /* @__PURE__ */ new Set();
91
- return dependencies.filter((dep) => {
92
- const hash = `${dep.name}@${dep.version}`;
93
- if (hashes.has(hash)) {
94
- return false;
95
- } else {
96
- hashes.add(hash);
97
- return true;
98
- }
99
- });
100
- }
101
-
102
- // src/core/package-managers/bun.ts
103
- var bun = {
104
- overridesKey: "overrides",
105
- // But also supports "resolutions"
106
- downloadDependency(...args) {
107
- return npm.downloadDependency(...args);
108
- },
109
- async listDependencies(options) {
110
- const args = ["pm", "ls"];
111
- if (options?.all) {
112
- args.push("--all");
113
- }
114
- const { execa } = await import("./execa-QLUM2B3W.js");
115
- const res = await execa("bun", args, { cwd: options?.cwd });
116
- return dedupeDependencies(
117
- res.stdout.split("\n").slice(1).map((line) => line.trim()).map((line) => /.* (@?\S+)@(\S+)$/.exec(line)).filter((match) => !!match).map(([_, name, version2]) => ({ name, version: version2 }))
118
- );
119
- }
120
- };
121
-
122
- // src/core/package-managers/yarn.ts
123
- var yarn = {
124
- overridesKey: "resolutions",
125
- downloadDependency(...args) {
126
- return npm.downloadDependency(...args);
127
- },
128
- async listDependencies(options) {
129
- const args = ["list", "--json"];
130
- if (options?.all) {
131
- args.push("--depth", "Infinity");
132
- }
133
- const { execa } = await import("./execa-QLUM2B3W.js");
134
- const res = await execa("yarn", args, { cwd: options?.cwd });
135
- const tree = res.stdout.split("\n").map((line) => JSON.parse(line)).find((line) => line.type === "tree")?.data;
136
- if (tree == null) throw Error("'yarn list --json' did not output a tree");
137
- const queue = [...tree.trees];
138
- const dependencies = [];
139
- while (queue.length > 0) {
140
- const { name: treeName, children } = queue.pop();
141
- const match = /(@?\S+)@(\S+)$/.exec(treeName);
142
- if (match) {
143
- const [_, name, version2] = match;
144
- dependencies.push({ name, version: version2 });
145
- }
146
- if (children != null) {
147
- queue.push(...children);
148
- }
149
- }
150
- return dedupeDependencies(dependencies);
151
- }
152
- };
153
-
154
- // src/core/package-managers/pnpm.ts
155
- var pnpm = {
156
- overridesKey: "resolutions",
157
- // "pnpm.overrides" has a higher priority, but I don't want to deal with nesting
158
- downloadDependency(...args) {
159
- return npm.downloadDependency(...args);
160
- },
161
- async listDependencies(options) {
162
- const args = ["ls", "-r", "--json"];
163
- if (options?.all) {
164
- args.push("--depth", "Infinity");
165
- }
166
- if (typeof process !== "undefined" && process.env.WXT_PNPM_IGNORE_WORKSPACE === "true") {
167
- args.push("--ignore-workspace");
168
- }
169
- const { execa } = await import("./execa-QLUM2B3W.js");
170
- const res = await execa("pnpm", args, { cwd: options?.cwd });
171
- const projects = JSON.parse(res.stdout);
172
- return flattenNpmListOutput(projects);
173
- }
174
- };
175
-
176
- // src/core/package-managers/index.ts
177
- async function createWxtPackageManager(root) {
178
- const pm = await detectPackageManager(root, {
179
- includeParentDirs: true
180
- });
181
- const requirePm = (cb) => {
182
- if (pm == null) throw Error("Could not detect package manager");
183
- return cb(pm);
184
- };
185
- return {
186
- get name() {
187
- return requirePm((pm2) => pm2.name);
188
- },
189
- get command() {
190
- return requirePm((pm2) => pm2.command);
191
- },
192
- get version() {
193
- return requirePm((pm2) => pm2.version);
194
- },
195
- get majorVersion() {
196
- return requirePm((pm2) => pm2.majorVersion);
197
- },
198
- get lockFile() {
199
- return requirePm((pm2) => pm2.lockFile);
200
- },
201
- get files() {
202
- return requirePm((pm2) => pm2.files);
203
- },
204
- addDependency,
205
- addDevDependency,
206
- ensureDependencyInstalled,
207
- installDependencies,
208
- removeDependency,
209
- get overridesKey() {
210
- return requirePm((pm2) => packageManagers[pm2.name].overridesKey);
211
- },
212
- downloadDependency(...args) {
213
- return requirePm(
214
- (pm2) => packageManagers[pm2.name].downloadDependency(...args)
215
- );
216
- },
217
- listDependencies(...args) {
218
- return requirePm(
219
- (pm2) => packageManagers[pm2.name].listDependencies(...args)
220
- );
221
- }
222
- };
223
- }
224
- var packageManagers = {
225
- npm,
226
- pnpm,
227
- bun,
228
- yarn
229
- };
230
-
231
- // src/core/utils/entrypoints.ts
232
- import path2, { relative, resolve } from "node:path";
233
- function getEntrypointName(entrypointsDir, inputPath) {
234
- const relativePath = path2.relative(entrypointsDir, inputPath);
235
- const name = relativePath.split(/[\.\/\\]/, 2)[0];
236
- return name;
237
- }
238
- function getEntrypointOutputFile(entrypoint, ext) {
239
- return resolve(entrypoint.outputDir, `${entrypoint.name}${ext}`);
240
- }
241
- function getEntrypointBundlePath(entrypoint, outDir, ext) {
242
- return normalizePath(
243
- relative(outDir, getEntrypointOutputFile(entrypoint, ext))
244
- );
245
- }
246
- function resolvePerBrowserOption(option, browser) {
247
- if (typeof option === "object" && !Array.isArray(option))
248
- return option[browser];
249
- return option;
250
- }
251
- function resolvePerBrowserOptions(options, browser) {
252
- return Object.fromEntries(
253
- Object.entries(options).map(([key, value]) => [
254
- key,
255
- key === "defaultIcon" ? value : resolvePerBrowserOption(value, browser)
256
- ])
257
- );
258
- }
259
- function isHtmlEntrypoint(entrypoint) {
260
- return entrypoint.inputPath.endsWith(".html");
261
- }
262
-
263
- // src/core/builders/vite/plugins/devHtmlPrerender.ts
264
- import { parseHTML } from "linkedom";
265
- import { dirname, relative as relative2, resolve as resolve2 } from "node:path";
266
- import { murmurHash } from "ohash";
267
- var inlineScriptContents = {};
268
- function devHtmlPrerender(config, server) {
269
- const htmlReloadId = "@wxt/reload-html";
270
- const resolvedHtmlReloadId = resolve2(
271
- config.wxtModuleDir,
272
- "dist/virtual/reload-html.js"
273
- );
274
- const virtualInlineScript = "virtual:wxt-inline-script";
275
- const resolvedVirtualInlineScript = "\0" + virtualInlineScript;
276
- return [
277
- {
278
- apply: "build",
279
- name: "wxt:dev-html-prerender",
280
- config() {
281
- return {
282
- resolve: {
283
- alias: {
284
- [htmlReloadId]: resolvedHtmlReloadId
285
- }
286
- }
287
- };
288
- },
289
- // Convert scripts like src="./main.tsx" -> src="http://localhost:3000/entrypoints/popup/main.tsx"
290
- // before the paths are replaced with their bundled path
291
- transform(code, id) {
292
- if (config.command !== "serve" || server == null || !id.endsWith(".html"))
293
- return;
294
- const { document } = parseHTML(code);
295
- const _pointToDevServer = (querySelector, attr) => pointToDevServer(config, server, id, document, querySelector, attr);
296
- _pointToDevServer("script[type=module]", "src");
297
- _pointToDevServer("link[rel=stylesheet]", "href");
298
- const reloader = document.createElement("script");
299
- reloader.src = htmlReloadId;
300
- reloader.type = "module";
301
- document.head.appendChild(reloader);
302
- const newHtml = document.toString();
303
- config.logger.debug("transform " + id);
304
- config.logger.debug("Old HTML:\n" + code);
305
- config.logger.debug("New HTML:\n" + newHtml);
306
- return newHtml;
307
- },
308
- // Pass the HTML through the dev server to add dev-mode specific code
309
- async transformIndexHtml(html, ctx) {
310
- if (config.command !== "serve" || server == null) return;
311
- const originalUrl = `${server.origin}${ctx.path}`;
312
- const name = getEntrypointName(config.entrypointsDir, ctx.filename);
313
- const url = `${server.origin}/${name}.html`;
314
- const serverHtml = await server.transformHtml(url, html, originalUrl);
315
- const { document } = parseHTML(serverHtml);
316
- const inlineScripts = document.querySelectorAll("script:not([src])");
317
- inlineScripts.forEach((script) => {
318
- const textContent = script.textContent ?? "";
319
- const hash = murmurHash(textContent);
320
- inlineScriptContents[hash] = textContent;
321
- const virtualScript = document.createElement("script");
322
- virtualScript.type = "module";
323
- virtualScript.src = `${server.origin}/@id/${virtualInlineScript}?${hash}`;
324
- script.replaceWith(virtualScript);
325
- });
326
- const viteClientScript = document.querySelector(
327
- "script[src='/@vite/client']"
328
- );
329
- if (viteClientScript) {
330
- viteClientScript.src = `${server.origin}${viteClientScript.src}`;
331
- }
332
- const newHtml = document.toString();
333
- config.logger.debug("transformIndexHtml " + ctx.filename);
334
- config.logger.debug("Old HTML:\n" + html);
335
- config.logger.debug("New HTML:\n" + newHtml);
336
- return newHtml;
337
- }
338
- },
339
- {
340
- name: "wxt:virtualize-react-refresh",
341
- apply: "serve",
342
- resolveId(id) {
343
- if (id.startsWith(virtualInlineScript)) {
344
- return "\0" + id;
345
- }
346
- if (id.startsWith("/chunks/")) {
347
- return "\0noop";
348
- }
349
- },
350
- load(id) {
351
- if (id.startsWith(resolvedVirtualInlineScript)) {
352
- const hash = Number(id.substring(id.indexOf("?") + 1));
353
- return inlineScriptContents[hash];
354
- }
355
- if (id === "\0noop") {
356
- return "";
357
- }
358
- }
359
- }
360
- ];
361
- }
362
- function pointToDevServer(config, server, id, document, querySelector, attr) {
363
- document.querySelectorAll(querySelector).forEach((element) => {
364
- const src = element.getAttribute(attr);
365
- if (!src || isUrl(src)) return;
366
- let resolvedAbsolutePath;
367
- const matchingAlias = Object.entries(config.alias).find(
368
- ([key]) => src.startsWith(key)
369
- );
370
- if (matchingAlias) {
371
- const [alias, replacement] = matchingAlias;
372
- resolvedAbsolutePath = resolve2(
373
- config.root,
374
- src.replace(alias, replacement)
375
- );
376
- } else {
377
- resolvedAbsolutePath = resolve2(dirname(id), src);
378
- }
379
- if (resolvedAbsolutePath) {
380
- const relativePath = normalizePath(
381
- relative2(config.root, resolvedAbsolutePath)
382
- );
383
- if (relativePath.startsWith(".")) {
384
- let path11 = normalizePath(resolvedAbsolutePath);
385
- if (!path11.startsWith("/")) path11 = "/" + path11;
386
- element.setAttribute(attr, `${server.origin}/@fs${path11}`);
387
- } else {
388
- const url = new URL(relativePath, server.origin);
389
- element.setAttribute(attr, url.href);
390
- }
391
- }
392
- });
393
- }
394
- function isUrl(str) {
395
- try {
396
- new URL(str);
397
- return true;
398
- } catch {
399
- return false;
400
- }
401
- }
402
-
403
- // src/core/builders/vite/plugins/devServerGlobals.ts
404
- function devServerGlobals(config, server) {
405
- return {
406
- name: "wxt:dev-server-globals",
407
- config() {
408
- if (server == null || config.command == "build") return;
409
- return {
410
- define: {
411
- __DEV_SERVER_PROTOCOL__: JSON.stringify("ws:"),
412
- __DEV_SERVER_HOSTNAME__: JSON.stringify(server.hostname),
413
- __DEV_SERVER_PORT__: JSON.stringify(server.port)
414
- }
415
- };
416
- }
417
- };
418
- }
419
-
420
- // src/core/utils/network.ts
421
- import dns from "node:dns";
422
-
423
- // src/core/utils/time.ts
424
- function formatDuration(duration) {
425
- if (duration < 1e3) return `${duration} ms`;
426
- if (duration < 1e4) return `${(duration / 1e3).toFixed(3)} s`;
427
- if (duration < 6e4) return `${(duration / 1e3).toFixed(1)} s`;
428
- return `${(duration / 1e3).toFixed(0)} s`;
429
- }
430
- function withTimeout(promise, duration) {
431
- return new Promise((res, rej) => {
432
- const timeout = setTimeout(() => {
433
- rej(`Promise timed out after ${duration}ms`);
434
- }, duration);
435
- promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
436
- });
437
- }
438
-
439
- // src/core/utils/network.ts
440
- function isOffline() {
441
- const isOffline2 = new Promise((res) => {
442
- dns.resolve("google.com", (err) => {
443
- if (err == null) {
444
- res(false);
445
- } else {
446
- res(true);
447
- }
448
- });
449
- });
450
- return withTimeout(isOffline2, 1e3).catch(() => true);
451
- }
452
- async function isOnline() {
453
- const offline = await isOffline();
454
- return !offline;
455
- }
456
- async function fetchCached(url, config) {
457
- let content = "";
458
- if (await isOnline()) {
459
- const res = await fetch(url);
460
- if (res.status < 300) {
461
- content = await res.text();
462
- await config.fsCache.set(url, content);
463
- } else {
464
- config.logger.debug(
465
- `Failed to download "${url}", falling back to cache...`
466
- );
467
- }
468
- }
469
- if (!content) content = await config.fsCache.get(url) ?? "";
470
- if (!content)
471
- throw Error(
472
- `Offline and "${url}" has not been cached. Try again when online.`
473
- );
474
- return content;
475
- }
476
-
477
- // src/core/builders/vite/plugins/download.ts
478
- function download(config) {
479
- return {
480
- name: "wxt:download",
481
- resolveId(id) {
482
- if (id.startsWith("url:")) return "\0" + id;
483
- },
484
- async load(id) {
485
- if (!id.startsWith("\0url:")) return;
486
- const url = id.replace("\0url:", "");
487
- return await fetchCached(url, config);
488
- }
489
- };
490
- }
491
-
492
- // src/core/builders/vite/plugins/multipageMove.ts
493
- import { dirname as dirname2, extname, resolve as resolve3, join } from "node:path";
494
- import fs, { ensureDir as ensureDir2 } from "fs-extra";
495
- function multipageMove(entrypoints, config) {
496
- return {
497
- name: "wxt:multipage-move",
498
- async writeBundle(_, bundle) {
499
- for (const oldBundlePath in bundle) {
500
- const entrypoint = entrypoints.find(
501
- (entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath)
502
- );
503
- if (entrypoint == null) {
504
- config.logger.debug(
505
- `No entrypoint found for ${oldBundlePath}, leaving in chunks directory`
506
- );
507
- continue;
508
- }
509
- const newBundlePath = getEntrypointBundlePath(
510
- entrypoint,
511
- config.outDir,
512
- extname(oldBundlePath)
513
- );
514
- if (newBundlePath === oldBundlePath) {
515
- config.logger.debug(
516
- "HTML file is already in the correct location",
517
- oldBundlePath
518
- );
519
- continue;
520
- }
521
- const oldAbsPath = resolve3(config.outDir, oldBundlePath);
522
- const newAbsPath = resolve3(config.outDir, newBundlePath);
523
- await ensureDir2(dirname2(newAbsPath));
524
- await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
525
- const renamedChunk = {
526
- ...bundle[oldBundlePath],
527
- fileName: newBundlePath
528
- };
529
- delete bundle[oldBundlePath];
530
- bundle[newBundlePath] = renamedChunk;
531
- }
532
- removeEmptyDirs(config.outDir);
533
- }
534
- };
535
- }
536
- async function removeEmptyDirs(dir) {
537
- const files = await fs.readdir(dir);
538
- for (const file of files) {
539
- const filePath = join(dir, file);
540
- const stats = await fs.stat(filePath);
541
- if (stats.isDirectory()) {
542
- await removeEmptyDirs(filePath);
543
- }
544
- }
545
- try {
546
- await fs.rmdir(dir);
547
- } catch {
548
- }
549
- }
550
-
551
- // src/core/utils/virtual-modules.ts
552
- var virtualEntrypointTypes = [
553
- "content-script-main-world",
554
- "content-script-isolated-world",
555
- "background",
556
- "unlisted-script"
557
- ];
558
- var virtualEntrypointModuleNames = virtualEntrypointTypes.map(
559
- (name) => `${name}-entrypoint`
560
- );
561
- var virtualModuleNames = [
562
- ...virtualEntrypointModuleNames,
563
- "mock-browser",
564
- "reload-html"
565
- ];
566
-
567
- // src/core/builders/vite/plugins/resolveVirtualModules.ts
568
- import fs2 from "fs-extra";
569
- import { resolve as resolve4 } from "path";
570
- function resolveVirtualModules(config) {
571
- return virtualModuleNames.map((name) => {
572
- const virtualId = `virtual:wxt-${name}?`;
573
- const resolvedVirtualId = "\0" + virtualId;
574
- return {
575
- name: `wxt:resolve-virtual-${name}`,
576
- resolveId(id) {
577
- const index = id.indexOf(virtualId);
578
- if (index === -1) return;
579
- const inputPath = normalizePath(id.substring(index + virtualId.length));
580
- return resolvedVirtualId + inputPath;
581
- },
582
- async load(id) {
583
- if (!id.startsWith(resolvedVirtualId)) return;
584
- const inputPath = id.replace(resolvedVirtualId, "");
585
- const template = await fs2.readFile(
586
- resolve4(config.wxtModuleDir, `dist/virtual/${name}.js`),
587
- "utf-8"
588
- );
589
- return template.replace(`virtual:user-${name}`, inputPath);
590
- }
591
- };
592
- });
593
- }
594
-
595
- // src/core/builders/vite/plugins/tsconfigPaths.ts
596
- function tsconfigPaths(config) {
597
- return {
598
- name: "wxt:aliases",
599
- async config() {
600
- return {
601
- resolve: {
602
- alias: config.alias
603
- }
604
- };
605
- }
606
- };
607
- }
608
-
609
- // src/core/utils/constants.ts
610
- var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
611
-
612
- // src/core/builders/vite/plugins/noopBackground.ts
613
- function noopBackground() {
614
- const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
615
- const resolvedVirtualModuleId = "\0" + virtualModuleId;
616
- return {
617
- name: "wxt:noop-background",
618
- resolveId(id) {
619
- if (id === virtualModuleId) return resolvedVirtualModuleId;
620
- },
621
- load(id) {
622
- if (id === resolvedVirtualModuleId) {
623
- return `import { defineBackground } from 'wxt/sandbox';
624
- export default defineBackground(() => void 0)`;
625
- }
626
- }
627
- };
628
- }
629
-
630
- // src/core/builders/vite/plugins/cssEntrypoints.ts
631
- function cssEntrypoints(entrypoint, config) {
632
- return {
633
- name: "wxt:css-entrypoint",
634
- config() {
635
- return {
636
- build: {
637
- rollupOptions: {
638
- output: {
639
- assetFileNames: () => getEntrypointBundlePath(entrypoint, config.outDir, ".css")
640
- }
641
- }
642
- }
643
- };
644
- },
645
- generateBundle(_, bundle) {
646
- Object.keys(bundle).forEach((file) => {
647
- if (file.endsWith(".js")) delete bundle[file];
648
- });
649
- }
650
- };
651
- }
652
-
653
- // src/core/builders/vite/plugins/bundleAnalysis.ts
654
- import { visualizer } from "@aklinker1/rollup-plugin-visualizer";
655
- import path3 from "node:path";
656
- var increment = 0;
657
- function bundleAnalysis(config) {
658
- return visualizer({
659
- template: "raw-data",
660
- filename: path3.resolve(
661
- config.analysis.outputDir,
662
- `${config.analysis.outputName}-${increment++}.json`
663
- )
664
- });
665
- }
666
-
667
- // src/core/utils/globals.ts
668
- function getGlobals(config) {
669
- return [
670
- {
671
- name: "MANIFEST_VERSION",
672
- value: config.manifestVersion,
673
- type: `2 | 3`
674
- },
675
- {
676
- name: "BROWSER",
677
- value: config.browser,
678
- type: `string`
679
- },
680
- {
681
- name: "CHROME",
682
- value: config.browser === "chrome",
683
- type: `boolean`
684
- },
685
- {
686
- name: "FIREFOX",
687
- value: config.browser === "firefox",
688
- type: `boolean`
689
- },
690
- {
691
- name: "SAFARI",
692
- value: config.browser === "safari",
693
- type: `boolean`
694
- },
695
- {
696
- name: "EDGE",
697
- value: config.browser === "edge",
698
- type: `boolean`
699
- },
700
- {
701
- name: "OPERA",
702
- value: config.browser === "opera",
703
- type: `boolean`
704
- },
705
- {
706
- name: "COMMAND",
707
- value: config.command,
708
- type: `"build" | "serve"`
709
- }
710
- ];
711
- }
712
- function getEntrypointGlobals(entrypointName) {
713
- return [
714
- {
715
- name: "ENTRYPOINT",
716
- value: entrypointName,
717
- type: `string`
718
- }
719
- ];
720
- }
721
-
722
- // src/core/builders/vite/plugins/globals.ts
723
- function globals(config) {
724
- return {
725
- name: "wxt:globals",
726
- config() {
727
- const define = {};
728
- for (const global of getGlobals(config)) {
729
- define[`import.meta.env.${global.name}`] = JSON.stringify(global.value);
730
- }
731
- return {
732
- define
733
- };
734
- }
735
- };
736
- }
737
-
738
- // src/core/builders/vite/plugins/webextensionPolyfillMock.ts
739
- import path4 from "node:path";
740
- function webextensionPolyfillMock(config) {
741
- return {
742
- name: "wxt:testing-inline-deps",
743
- config() {
744
- return {
745
- resolve: {
746
- alias: {
747
- // Alias to use a mocked version of the polyfill
748
- "webextension-polyfill": path4.resolve(
749
- config.wxtModuleDir,
750
- "dist/virtual/mock-browser"
751
- )
752
- }
753
- },
754
- ssr: {
755
- // Inline all WXT modules
756
- noExternal: ["wxt"]
757
- }
758
- };
759
- }
760
- };
761
- }
762
-
763
- // src/core/builders/vite/plugins/excludeBrowserPolyfill.ts
764
- function excludeBrowserPolyfill(config) {
765
- const virtualId = "virtual:wxt-webextension-polyfill-disabled";
766
- return {
767
- name: "wxt:exclude-browser-polyfill",
768
- config() {
769
- if (config.experimental.includeBrowserPolyfill) return;
770
- return {
771
- resolve: {
772
- alias: {
773
- "webextension-polyfill": virtualId
774
- }
775
- }
776
- };
777
- },
778
- load(id) {
779
- if (id === virtualId) {
780
- return "export default chrome";
781
- }
782
- }
783
- };
784
- }
785
-
786
- // src/core/builders/vite/plugins/entrypointGroupGlobals.ts
787
- function entrypointGroupGlobals(entrypointGroup) {
788
- return {
789
- name: "wxt:entrypoint-group-globals",
790
- config() {
791
- const define = {};
792
- let name = Array.isArray(entrypointGroup) ? "html" : entrypointGroup.name;
793
- for (const global of getEntrypointGlobals(name)) {
794
- define[`import.meta.env.${global.name}`] = JSON.stringify(global.value);
795
- }
796
- return {
797
- define
798
- };
799
- }
800
- };
801
- }
802
-
803
- // src/core/builders/vite/plugins/defineImportMeta.ts
804
- function defineImportMeta() {
805
- return {
806
- name: "wxt:define",
807
- config() {
808
- return {
809
- define: {
810
- // This works for all extension contexts, including background service worker
811
- "import.meta.url": "self.location.href"
812
- }
813
- };
814
- }
815
- };
816
- }
817
-
818
- // src/core/utils/transform.ts
819
- import { parseModule } from "magicast";
820
- function removeMainFunctionCode(code) {
821
- const mod = parseModule(code);
822
- emptyMainFunction(mod);
823
- return mod.generate();
824
- }
825
- function emptyMainFunction(mod) {
826
- if (mod.exports?.default?.$type === "function-call") {
827
- if (mod.exports.default.$ast?.arguments?.[0]?.body) {
828
- mod.exports.default.$ast.arguments[0].body.body = [];
829
- } else if (mod.exports.default.$ast?.arguments?.[0]?.properties) {
830
- mod.exports.default.$ast.arguments[0].properties = mod.exports.default.$ast.arguments[0].properties.filter(
831
- (prop) => prop.key.name !== "main"
832
- );
833
- }
834
- }
835
- }
836
-
837
- // src/core/builders/vite/plugins/removeEntrypointMainFunction.ts
838
- import { resolve as resolve5 } from "node:path";
839
- function removeEntrypointMainFunction(config, path11) {
840
- const absPath = normalizePath(resolve5(config.root, path11));
841
- return {
842
- name: "wxt:remove-entrypoint-main-function",
843
- transform(code, id) {
844
- if (id === absPath) return removeMainFunctionCode(code);
845
- }
846
- };
847
- }
848
-
849
- // src/core/builders/vite/plugins/wxtPluginLoader.ts
850
- import { parseHTML as parseHTML2 } from "linkedom";
851
- function wxtPluginLoader(config) {
852
- const virtualModuleId = "virtual:wxt-plugins";
853
- const resolvedVirtualModuleId = "\0" + virtualModuleId;
854
- const virtualHtmlModuleId = "virtual:wxt-html-plugins";
855
- const resolvedVirtualHtmlModuleId = "\0" + virtualHtmlModuleId;
856
- return {
857
- name: "wxt:plugin-loader",
858
- resolveId(id) {
859
- if (id === virtualModuleId) return resolvedVirtualModuleId;
860
- if (id === virtualHtmlModuleId) return resolvedVirtualHtmlModuleId;
861
- },
862
- load(id) {
863
- if (id === resolvedVirtualModuleId) {
864
- const imports = config.plugins.map(
865
- (plugin, i) => `import initPlugin${i} from '${normalizePath(plugin)}';`
866
- ).join("\n");
867
- const initCalls = config.plugins.map((_, i) => ` initPlugin${i}();`).join("\n");
868
- return `${imports}
869
-
870
- export function initPlugins() {
871
- ${initCalls}
872
- }`;
873
- }
874
- if (id === resolvedVirtualHtmlModuleId) {
875
- return `import { initPlugins } from '${virtualModuleId}';
876
-
877
- try {
878
- initPlugins();
879
- } catch (err) {
880
- console.error("[wxt] Failed to initialize plugins", err);
881
- }`;
882
- }
883
- },
884
- transformIndexHtml: {
885
- // Use "pre" so the new script is added before vite bundles all the scripts
886
- order: "pre",
887
- handler(html, _ctx) {
888
- const src = config.command === "serve" ? `http://${config.dev.server?.hostname}:${config.dev.server?.port}/@id/${virtualHtmlModuleId}` : virtualHtmlModuleId;
889
- const { document } = parseHTML2(html);
890
- const existing = document.querySelector(`script[src='${src}']`);
891
- if (existing) return;
892
- const script = document.createElement("script");
893
- script.type = "module";
894
- script.src = src;
895
- if (document.head == null) {
896
- const newHead = document.createElement("head");
897
- document.documentElement.prepend(newHead);
898
- }
899
- document.head.prepend(script);
900
- return document.toString();
901
- }
902
- }
903
- };
904
- }
905
-
906
- // src/core/builders/vite/plugins/resolveAppConfig.ts
907
- import { exists } from "fs-extra";
908
- import { resolve as resolve6 } from "node:path";
909
- function resolveAppConfig(config) {
910
- const virtualModuleId = "virtual:app-config";
911
- const resolvedVirtualModuleId = "\0" + virtualModuleId;
912
- const appConfigFile = resolve6(config.srcDir, "app.config.ts");
913
- return {
914
- name: "wxt:resolve-app-config",
915
- async resolveId(id) {
916
- if (id !== virtualModuleId) return;
917
- return await exists(appConfigFile) ? appConfigFile : resolvedVirtualModuleId;
918
- },
919
- load(id) {
920
- if (id === resolvedVirtualModuleId) return `export default {}`;
921
- }
922
- };
923
- }
924
-
925
- // src/core/utils/arrays.ts
926
- function every(array, predicate) {
927
- for (let i = 0; i < array.length; i++)
928
- if (!predicate(array[i], i)) return false;
929
- return true;
930
- }
931
- function some(array, predicate) {
932
- for (let i = 0; i < array.length; i++)
933
- if (predicate(array[i], i)) return true;
934
- return false;
935
- }
936
- function toArray(a) {
937
- return Array.isArray(a) ? a : [a];
938
- }
939
- function filterTruthy(array) {
940
- return array.filter((item) => !!item);
941
- }
942
-
943
- // src/core/utils/strings.ts
944
- function kebabCaseAlphanumeric(str) {
945
- return str.toLowerCase().replace(/[^a-z0-9-\s]/g, "").replace(/\s+/g, "-");
946
- }
947
- function safeVarName(str) {
948
- return "_" + kebabCaseAlphanumeric(str.trim()).replace("-", "_");
949
- }
950
- function removeImportStatements(text) {
951
- return text.replace(
952
- /(import\s?[\s\S]*?from\s?["'][\s\S]*?["'];?|import\s?["'][\s\S]*?["'];?)/gm,
953
- ""
954
- );
955
- }
956
- function removeProjectImportStatements(text) {
957
- const noImports = removeImportStatements(text);
958
- return `import { defineUnlistedScript, defineContentScript, defineBackground } from 'wxt/sandbox';
959
-
960
- ${noImports}`;
961
- }
962
-
963
- // src/core/builders/vite/index.ts
964
- import { ViteNodeServer } from "vite-node/server";
965
- import { ViteNodeRunner } from "vite-node/client";
966
- import { installSourcemapsSupport } from "vite-node/source-map";
967
- async function createViteBuilder(wxtConfig, hooks, server) {
968
- const vite2 = await import("vite");
969
- const getBaseConfig = async () => {
970
- const config = await wxtConfig.vite(wxtConfig.env);
971
- config.root = wxtConfig.root;
972
- config.configFile = false;
973
- config.logLevel = "warn";
974
- config.mode = wxtConfig.mode;
975
- config.build ??= {};
976
- config.publicDir = wxtConfig.publicDir;
977
- config.build.copyPublicDir = false;
978
- config.build.outDir = wxtConfig.outDir;
979
- config.build.emptyOutDir = false;
980
- if (config.build.minify == null && wxtConfig.command === "serve") {
981
- config.build.minify = false;
982
- }
983
- if (config.build.sourcemap == null && wxtConfig.command === "serve") {
984
- config.build.sourcemap = "inline";
985
- }
986
- config.plugins ??= [];
987
- config.plugins.push(
988
- download(wxtConfig),
989
- devHtmlPrerender(wxtConfig, server),
990
- resolveVirtualModules(wxtConfig),
991
- devServerGlobals(wxtConfig, server),
992
- tsconfigPaths(wxtConfig),
993
- noopBackground(),
994
- globals(wxtConfig),
995
- excludeBrowserPolyfill(wxtConfig),
996
- defineImportMeta(),
997
- wxtPluginLoader(wxtConfig),
998
- resolveAppConfig(wxtConfig)
999
- );
1000
- if (wxtConfig.analysis.enabled) {
1001
- config.plugins.push(bundleAnalysis(wxtConfig));
1002
- }
1003
- return config;
1004
- };
1005
- const getLibModeConfig = (entrypoint) => {
1006
- const entry = getRollupEntry(entrypoint);
1007
- const plugins = [
1008
- entrypointGroupGlobals(entrypoint)
1009
- ];
1010
- if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
1011
- plugins.push(cssEntrypoints(entrypoint, wxtConfig));
1012
- }
1013
- const iifeReturnValueName = safeVarName(entrypoint.name);
1014
- const libMode = {
1015
- mode: wxtConfig.mode,
1016
- plugins,
1017
- esbuild: {
1018
- // Add a footer with the returned value so it can return values to `scripting.executeScript`
1019
- // Footer is added apart of esbuild to make sure it's not minified. It
1020
- // get's removed if added to `build.rollupOptions.output.footer`
1021
- // See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
1022
- footer: iifeReturnValueName + ";"
1023
- },
1024
- build: {
1025
- lib: {
1026
- entry,
1027
- formats: ["iife"],
1028
- name: iifeReturnValueName,
1029
- fileName: entrypoint.name
1030
- },
1031
- rollupOptions: {
1032
- output: {
1033
- // There's only a single output for this build, so we use the desired bundle path for the
1034
- // entry output (like "content-scripts/overlay.js")
1035
- entryFileNames: getEntrypointBundlePath(
1036
- entrypoint,
1037
- wxtConfig.outDir,
1038
- ".js"
1039
- ),
1040
- // Output content script CSS to `content-scripts/`, but all other scripts are written to
1041
- // `assets/`.
1042
- assetFileNames: ({ name }) => {
1043
- if (entrypoint.type === "content-script" && name?.endsWith("css")) {
1044
- return `content-scripts/${entrypoint.name}.[ext]`;
1045
- } else {
1046
- return `assets/${entrypoint.name}.[ext]`;
1047
- }
1048
- }
1049
- }
1050
- }
1051
- },
1052
- define: {
1053
- // See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
1054
- "process.env.NODE_ENV": JSON.stringify(wxtConfig.mode)
1055
- }
1056
- };
1057
- return libMode;
1058
- };
1059
- const getMultiPageConfig = (entrypoints) => {
1060
- const htmlEntrypoints = new Set(
1061
- entrypoints.filter(isHtmlEntrypoint).map((e) => e.name)
1062
- );
1063
- return {
1064
- mode: wxtConfig.mode,
1065
- plugins: [
1066
- multipageMove(entrypoints, wxtConfig),
1067
- entrypointGroupGlobals(entrypoints)
1068
- ],
1069
- build: {
1070
- rollupOptions: {
1071
- input: entrypoints.reduce((input, entry) => {
1072
- input[entry.name] = getRollupEntry(entry);
1073
- return input;
1074
- }, {}),
1075
- output: {
1076
- // Include a hash to prevent conflicts
1077
- chunkFileNames: "chunks/[name]-[hash].js",
1078
- entryFileNames: ({ name }) => {
1079
- if (htmlEntrypoints.has(name)) return "chunks/[name]-[hash].js";
1080
- return "[name].js";
1081
- },
1082
- // We can't control the "name", so we need a hash to prevent conflicts
1083
- assetFileNames: "assets/[name]-[hash].[ext]"
1084
- }
1085
- }
1086
- }
1087
- };
1088
- };
1089
- const getCssConfig = (entrypoint) => {
1090
- return {
1091
- mode: wxtConfig.mode,
1092
- plugins: [entrypointGroupGlobals(entrypoint)],
1093
- build: {
1094
- rollupOptions: {
1095
- input: {
1096
- [entrypoint.name]: entrypoint.inputPath
1097
- },
1098
- output: {
1099
- assetFileNames: () => {
1100
- if (entrypoint.type === "content-script-style") {
1101
- return `content-scripts/${entrypoint.name}.[ext]`;
1102
- } else {
1103
- return `assets/${entrypoint.name}.[ext]`;
1104
- }
1105
- }
1106
- }
1107
- }
1108
- }
1109
- };
1110
- };
1111
- return {
1112
- name: "Vite",
1113
- version: vite2.version,
1114
- async importEntrypoint(path11) {
1115
- switch (wxtConfig.experimental.entrypointImporter) {
1116
- default:
1117
- case "jiti": {
1118
- return await importEntrypointFile(path11);
1119
- }
1120
- case "vite-runtime": {
1121
- const baseConfig = await getBaseConfig();
1122
- const envConfig = {
1123
- plugins: [
1124
- webextensionPolyfillMock(wxtConfig),
1125
- removeEntrypointMainFunction(wxtConfig, path11)
1126
- ]
1127
- };
1128
- const config = vite2.mergeConfig(baseConfig, envConfig);
1129
- const server2 = await vite2.createServer(config);
1130
- await server2.listen();
1131
- const runtime = await vite2.createViteRuntime(server2, { hmr: false });
1132
- const module = await runtime.executeUrl(path11);
1133
- await server2.close();
1134
- return module.default;
1135
- }
1136
- case "vite-node": {
1137
- const baseConfig = await getBaseConfig();
1138
- baseConfig.optimizeDeps ??= {};
1139
- baseConfig.optimizeDeps.noDiscovery = true;
1140
- baseConfig.optimizeDeps.include = [];
1141
- const envConfig = {
1142
- plugins: [
1143
- webextensionPolyfillMock(wxtConfig),
1144
- removeEntrypointMainFunction(wxtConfig, path11)
1145
- ]
1146
- };
1147
- const config = vite2.mergeConfig(baseConfig, envConfig);
1148
- const server2 = await vite2.createServer(config);
1149
- await server2.pluginContainer.buildStart({});
1150
- const node = new ViteNodeServer(
1151
- // @ts-ignore: Some weird type error...
1152
- server2
1153
- );
1154
- installSourcemapsSupport({
1155
- getSourceMap: (source) => node.getSourceMap(source)
1156
- });
1157
- const runner = new ViteNodeRunner({
1158
- root: server2.config.root,
1159
- base: server2.config.base,
1160
- // when having the server and runner in a different context,
1161
- // you will need to handle the communication between them
1162
- // and pass to this function
1163
- fetchModule(id) {
1164
- return node.fetchModule(id);
1165
- },
1166
- resolveId(id, importer) {
1167
- return node.resolveId(id, importer);
1168
- }
1169
- });
1170
- const res = await runner.executeFile(path11);
1171
- await server2.close();
1172
- return res.default;
1173
- }
1174
- }
1175
- },
1176
- async build(group) {
1177
- let entryConfig;
1178
- if (Array.isArray(group)) entryConfig = getMultiPageConfig(group);
1179
- else if (group.inputPath.endsWith(".css"))
1180
- entryConfig = getCssConfig(group);
1181
- else entryConfig = getLibModeConfig(group);
1182
- const buildConfig = vite2.mergeConfig(await getBaseConfig(), entryConfig);
1183
- await hooks.callHook(
1184
- "vite:build:extendConfig",
1185
- toArray(group),
1186
- buildConfig
1187
- );
1188
- const result = await vite2.build(buildConfig);
1189
- return {
1190
- entrypoints: group,
1191
- chunks: getBuildOutputChunks(result)
1192
- };
1193
- },
1194
- async createServer(info) {
1195
- const serverConfig = {
1196
- server: {
1197
- port: info.port,
1198
- strictPort: true,
1199
- host: info.hostname,
1200
- origin: info.origin
1201
- }
1202
- };
1203
- const baseConfig = await getBaseConfig();
1204
- const finalConfig = vite2.mergeConfig(baseConfig, serverConfig);
1205
- await hooks.callHook("vite:devServer:extendConfig", finalConfig);
1206
- const viteServer = await vite2.createServer(finalConfig);
1207
- const server2 = {
1208
- async listen() {
1209
- await viteServer.listen(info.port);
1210
- },
1211
- async close() {
1212
- await viteServer.close();
1213
- },
1214
- transformHtml(...args) {
1215
- return viteServer.transformIndexHtml(...args);
1216
- },
1217
- ws: {
1218
- send(message, payload) {
1219
- return viteServer.ws.send(message, payload);
1220
- },
1221
- on(message, cb) {
1222
- viteServer.ws.on(message, cb);
1223
- }
1224
- },
1225
- watcher: viteServer.watcher
1226
- };
1227
- return server2;
1228
- }
1229
- };
1230
- }
1231
- function getBuildOutputChunks(result) {
1232
- if ("on" in result) throw Error("wxt does not support vite watch mode.");
1233
- if (Array.isArray(result)) return result.flatMap(({ output }) => output);
1234
- return result.output;
1235
- }
1236
- function getRollupEntry(entrypoint) {
1237
- let virtualEntrypointType;
1238
- switch (entrypoint.type) {
1239
- case "background":
1240
- case "unlisted-script":
1241
- virtualEntrypointType = entrypoint.type;
1242
- break;
1243
- case "content-script":
1244
- virtualEntrypointType = entrypoint.options.world === "MAIN" ? "content-script-main-world" : "content-script-isolated-world";
1245
- break;
1246
- }
1247
- if (virtualEntrypointType) {
1248
- const moduleId = `virtual:wxt-${virtualEntrypointType}-entrypoint`;
1249
- return `${moduleId}?${entrypoint.inputPath}`;
1250
- }
1251
- return entrypoint.inputPath;
1252
- }
1253
-
1254
- // src/modules.ts
1255
- import * as vite from "vite";
1256
- import glob from "fast-glob";
1257
- function defineWxtModule(module) {
1258
- if (typeof module === "function") return { setup: module };
1259
- return module;
1260
- }
1261
- function addViteConfig(wxt2, viteConfig) {
1262
- wxt2.hooks.hook("ready", (wxt3) => {
1263
- const userVite = wxt3.config.vite;
1264
- wxt3.config.vite = async (env) => {
1265
- const fromUser = await userVite(env);
1266
- const fromModule = viteConfig(env) ?? {};
1267
- return vite.mergeConfig(fromModule, fromUser);
1268
- };
1269
- });
1270
- }
1271
-
1272
- // src/builtin-modules/unimport.ts
1273
- import { createUnimport } from "unimport";
1274
- import { extname as extname2 } from "node:path";
1275
- var unimport_default = defineWxtModule({
1276
- name: "wxt:built-in:unimport",
1277
- setup(wxt2) {
1278
- const options = wxt2.config.imports;
1279
- if (options === false) return;
1280
- let unimport;
1281
- wxt2.hooks.hook("ready", () => {
1282
- const addModuleImports = (module) => {
1283
- if (!module.imports) return;
1284
- options.imports ??= [];
1285
- options.imports.push(...module.imports);
1286
- };
1287
- wxt2.config.builtinModules.forEach(addModuleImports);
1288
- wxt2.config.userModules.forEach(addModuleImports);
1289
- });
1290
- wxt2.hooks.afterEach((event) => {
1291
- if (event.name === "ready") {
1292
- unimport = createUnimport(options);
1293
- }
1294
- });
1295
- wxt2.hooks.hook("prepare:types", async (_, entries) => {
1296
- await unimport.init();
1297
- entries.push(await getImportsDeclarationEntry(unimport));
1298
- if (options.eslintrc.enabled === false) return;
1299
- entries.push(
1300
- await getEslintConfigEntry(unimport, options.eslintrc.enabled, options)
1301
- );
1302
- });
1303
- addViteConfig(wxt2, () => ({
1304
- plugins: [vitePlugin(unimport)]
1305
- }));
1306
- }
1307
- });
1308
- function vitePlugin(unimport) {
1309
- const ENABLED_EXTENSIONS = /* @__PURE__ */ new Set([
1310
- ".js",
1311
- ".jsx",
1312
- ".ts",
1313
- ".tsx",
1314
- ".vue",
1315
- ".svelte"
1316
- ]);
1317
- return {
1318
- name: "wxt:unimport",
1319
- async transform(code, id) {
1320
- if (id.includes("node_modules")) return;
1321
- if (!ENABLED_EXTENSIONS.has(extname2(id))) return;
1322
- const injected = await unimport.injectImports(code, id);
1323
- return {
1324
- code: injected.code,
1325
- map: injected.s.generateMap({ hires: "boundary", source: id })
1326
- };
1327
- }
1328
- };
1329
- }
1330
- async function getImportsDeclarationEntry(unimport) {
1331
- await unimport.init();
1332
- return {
1333
- path: "types/imports.d.ts",
1334
- text: [
1335
- "// Generated by wxt",
1336
- await unimport.generateTypeDeclarations(),
1337
- ""
1338
- ].join("\n"),
1339
- tsReference: true
1340
- };
1341
- }
1342
- async function getEslintConfigEntry(unimport, version2, options) {
1343
- const globals2 = (await unimport.getImports()).map((i) => i.as ?? i.name).filter(Boolean).sort().reduce((globals3, name) => {
1344
- globals3[name] = options.eslintrc.globalsPropValue;
1345
- return globals3;
1346
- }, {});
1347
- if (version2 <= 8) return getEslint8ConfigEntry(options, globals2);
1348
- else return getEslint9ConfigEntry(options, globals2);
1349
- }
1350
- function getEslint8ConfigEntry(options, globals2) {
1351
- return {
1352
- path: options.eslintrc.filePath,
1353
- text: JSON.stringify({ globals: globals2 }, null, 2) + "\n"
1354
- };
1355
- }
1356
- function getEslint9ConfigEntry(options, globals2) {
1357
- return {
1358
- path: options.eslintrc.filePath,
1359
- text: `const globals = ${JSON.stringify(globals2, null, 2)}
1360
-
1361
- export default {
1362
- name: "wxt/auto-imports",
1363
- languageOptions: {
1364
- globals,
1365
- sourceType: "module",
1366
- },
1367
- };
1368
- `
1369
- };
1370
- }
1371
-
1372
- // src/builtin-modules/index.ts
1373
- var builtinModules = [unimport_default];
1374
-
1375
- // src/core/wxt.ts
1376
- var wxt;
1377
- async function registerWxt(command, inlineConfig = {}, getServer) {
1378
- const hooks = createHooks();
1379
- const config = await resolveConfig(inlineConfig, command);
1380
- const server = await getServer?.(config);
1381
- const builder = await createViteBuilder(config, hooks, server);
1382
- const pm = await createWxtPackageManager(config.root);
1383
- wxt = {
1384
- config,
1385
- hooks,
1386
- get logger() {
1387
- return config.logger;
1388
- },
1389
- async reloadConfig() {
1390
- wxt.config = await resolveConfig(inlineConfig, command);
1391
- },
1392
- pm,
1393
- builder,
1394
- server
1395
- };
1396
- const initModule = async (module) => {
1397
- if (module.hooks) wxt.hooks.addHooks(module.hooks);
1398
- await module.setup?.(
1399
- wxt,
1400
- // @ts-expect-error: Untyped configKey field
1401
- module.configKey ? config[module.configKey] : void 0
1402
- );
1403
- };
1404
- for (const builtinModule of builtinModules) await initModule(builtinModule);
1405
- for (const userModule of config.userModules) await initModule(userModule);
1406
- wxt.hooks.addHooks(config.hooks);
1407
- await wxt.hooks.callHook("ready", wxt);
1408
- }
1409
-
1410
- // src/core/utils/fs.ts
1411
- async function writeFileIfDifferent(file, newContents) {
1412
- const existingContents = await fs3.readFile(file, "utf-8").catch(() => void 0);
1413
- if (existingContents !== newContents) {
1414
- await fs3.writeFile(file, newContents);
1415
- }
1416
- }
1417
- async function getPublicFiles() {
1418
- if (!await fs3.exists(wxt.config.publicDir)) return [];
1419
- const files = await glob2("**/*", { cwd: wxt.config.publicDir });
1420
- return files.map(unnormalizePath);
1421
- }
1422
-
1423
- // src/core/utils/building/build-entrypoints.ts
1424
- import fs4 from "fs-extra";
1425
- import { dirname as dirname3, resolve as resolve7 } from "path";
1426
- import pc from "picocolors";
1427
- async function buildEntrypoints(groups, spinner) {
1428
- const steps = [];
1429
- for (let i = 0; i < groups.length; i++) {
1430
- const group = groups[i];
1431
- const groupNames = toArray(group).map((e) => e.name);
1432
- const groupNameColored = groupNames.join(pc.dim(", "));
1433
- spinner.text = pc.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNameColored}`;
1434
- try {
1435
- steps.push(await wxt.builder.build(group));
1436
- } catch (err) {
1437
- spinner.stop().clear();
1438
- wxt.logger.error(err);
1439
- throw Error(`Failed to build ${groupNames.join(", ")}`, { cause: err });
1440
- }
1441
- }
1442
- const publicAssets = await copyPublicDirectory();
1443
- return { publicAssets, steps };
1444
- }
1445
- async function copyPublicDirectory() {
1446
- const files = (await getPublicFiles()).map((file) => ({
1447
- absoluteSrc: resolve7(wxt.config.publicDir, file),
1448
- relativeDest: file
1449
- }));
1450
- await wxt.hooks.callHook("build:publicAssets", wxt, files);
1451
- if (files.length === 0) return [];
1452
- const publicAssets = [];
1453
- for (const file of files) {
1454
- const absoluteDest = resolve7(wxt.config.outDir, file.relativeDest);
1455
- await fs4.ensureDir(dirname3(absoluteDest));
1456
- if ("absoluteSrc" in file) {
1457
- await fs4.copyFile(file.absoluteSrc, absoluteDest);
1458
- } else {
1459
- await fs4.writeFile(absoluteDest, file.contents, "utf8");
1460
- }
1461
- publicAssets.push({
1462
- type: "asset",
1463
- fileName: file.relativeDest
1464
- });
1465
- }
1466
- return publicAssets;
1467
- }
1468
-
1469
- // src/core/utils/building/detect-dev-changes.ts
1470
- function detectDevChanges(changedFiles, currentOutput) {
1471
- const isConfigChange = some(
1472
- changedFiles,
1473
- (file) => file === wxt.config.userConfigMetadata.configFile
1474
- );
1475
- if (isConfigChange) return { type: "full-restart" };
1476
- const isRunnerChange = some(
1477
- changedFiles,
1478
- (file) => file === wxt.config.runnerConfig.configFile
1479
- );
1480
- if (isRunnerChange) return { type: "browser-restart" };
1481
- const changedSteps = new Set(
1482
- changedFiles.flatMap(
1483
- (changedFile) => findEffectedSteps(changedFile, currentOutput)
1484
- )
1485
- );
1486
- if (changedSteps.size === 0) {
1487
- const hasPublicChange = some(
1488
- changedFiles,
1489
- (file) => file.startsWith(wxt.config.publicDir)
1490
- );
1491
- if (hasPublicChange) {
1492
- return {
1493
- type: "extension-reload",
1494
- rebuildGroups: [],
1495
- cachedOutput: currentOutput
1496
- };
1497
- } else {
1498
- return { type: "no-change" };
1499
- }
1500
- }
1501
- const unchangedOutput = {
1502
- manifest: currentOutput.manifest,
1503
- steps: [],
1504
- publicAssets: [...currentOutput.publicAssets]
1505
- };
1506
- const changedOutput = {
1507
- manifest: currentOutput.manifest,
1508
- steps: [],
1509
- publicAssets: []
1510
- };
1511
- for (const step of currentOutput.steps) {
1512
- if (changedSteps.has(step)) {
1513
- changedOutput.steps.push(step);
1514
- } else {
1515
- unchangedOutput.steps.push(step);
1516
- }
1517
- }
1518
- const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, (file) => file.endsWith(".html"));
1519
- if (isOnlyHtmlChanges) {
1520
- return {
1521
- type: "html-reload",
1522
- cachedOutput: unchangedOutput,
1523
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
1524
- };
1525
- }
1526
- const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
1527
- changedOutput.steps.flatMap((step) => step.entrypoints),
1528
- (entry) => entry.type === "content-script"
1529
- );
1530
- if (isOnlyContentScripts) {
1531
- return {
1532
- type: "content-script-reload",
1533
- cachedOutput: unchangedOutput,
1534
- changedSteps: changedOutput.steps,
1535
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
1536
- };
1537
- }
1538
- return {
1539
- type: "extension-reload",
1540
- cachedOutput: unchangedOutput,
1541
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
1542
- };
1543
- }
1544
- function findEffectedSteps(changedFile, currentOutput) {
1545
- const changes = [];
1546
- const changedPath = normalizePath(changedFile);
1547
- const isChunkEffected = (chunk) => (
1548
- // If it's an HTML file with the same path, is is effected because HTML files need to be re-rendered
1549
- // - fileName is normalized, relative bundle path, "<entrypoint-name>.html"
1550
- chunk.type === "asset" && changedPath.replace("/index.html", ".html").endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
1551
- // - moduleIds are absolute, normalized paths
1552
- chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
1553
- );
1554
- for (const step of currentOutput.steps) {
1555
- const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
1556
- if (effectedChunk) changes.push(step);
1557
- }
1558
- return changes;
1559
- }
1560
-
1561
- // src/core/utils/building/find-entrypoints.ts
1562
- import { relative as relative3, resolve as resolve8 } from "path";
1563
- import fs5 from "fs-extra";
1564
- import { minimatch } from "minimatch";
1565
- import { parseHTML as parseHTML3 } from "linkedom";
1566
- import JSON5 from "json5";
1567
- import glob3 from "fast-glob";
1568
- import pc2 from "picocolors";
1569
- async function findEntrypoints() {
1570
- await fs5.mkdir(wxt.config.wxtDir, { recursive: true });
1571
- await fs5.writeJson(resolve8(wxt.config.wxtDir, "tsconfig.json"), {});
1572
- const relativePaths = await glob3(Object.keys(PATH_GLOB_TO_TYPE_MAP), {
1573
- cwd: wxt.config.entrypointsDir
1574
- });
1575
- relativePaths.sort();
1576
- const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
1577
- const entrypointInfos = relativePaths.reduce((results, relativePath) => {
1578
- const inputPath = resolve8(wxt.config.entrypointsDir, relativePath);
1579
- const name = getEntrypointName(wxt.config.entrypointsDir, inputPath);
1580
- const matchingGlob = pathGlobs.find(
1581
- (glob8) => minimatch(relativePath, glob8)
1582
- );
1583
- if (matchingGlob) {
1584
- const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
1585
- results.push({
1586
- name,
1587
- inputPath,
1588
- type,
1589
- skipped: wxt.config.filterEntrypoints != null && !wxt.config.filterEntrypoints.has(name)
1590
- });
1591
- }
1592
- return results;
1593
- }, []);
1594
- preventNoEntrypoints(entrypointInfos);
1595
- preventDuplicateEntrypointNames(entrypointInfos);
1596
- let hasBackground = false;
1597
- const entrypoints = await Promise.all(
1598
- entrypointInfos.map(async (info) => {
1599
- const { type } = info;
1600
- switch (type) {
1601
- case "popup":
1602
- return await getPopupEntrypoint(info);
1603
- case "sidepanel":
1604
- return await getSidepanelEntrypoint(info);
1605
- case "options":
1606
- return await getOptionsEntrypoint(info);
1607
- case "background":
1608
- hasBackground = true;
1609
- return await getBackgroundEntrypoint(info);
1610
- case "content-script":
1611
- return await getContentScriptEntrypoint(info);
1612
- case "unlisted-page":
1613
- return await getUnlistedPageEntrypoint(info);
1614
- case "unlisted-script":
1615
- return await getUnlistedScriptEntrypoint(info);
1616
- case "content-script-style":
1617
- return {
1618
- ...info,
1619
- type,
1620
- outputDir: resolve8(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
1621
- options: {
1622
- include: void 0,
1623
- exclude: void 0
1624
- }
1625
- };
1626
- default:
1627
- return {
1628
- ...info,
1629
- type,
1630
- outputDir: wxt.config.outDir,
1631
- options: {
1632
- include: void 0,
1633
- exclude: void 0
1634
- }
1635
- };
1636
- }
1637
- })
1638
- );
1639
- if (wxt.config.command === "serve" && !hasBackground) {
1640
- entrypoints.push(
1641
- await getBackgroundEntrypoint({
1642
- inputPath: VIRTUAL_NOOP_BACKGROUND_MODULE_ID,
1643
- name: "background",
1644
- type: "background",
1645
- skipped: false
1646
- })
1647
- );
1648
- }
1649
- wxt.logger.debug("All entrypoints:", entrypoints);
1650
- const skippedEntrypointNames = entrypointInfos.filter((item) => item.skipped).map((item) => item.name);
1651
- if (skippedEntrypointNames.length) {
1652
- wxt.logger.warn(
1653
- `Filter excluded the following entrypoints:
1654
- ${skippedEntrypointNames.map((item) => `${pc2.dim("-")} ${pc2.cyan(item)}`).join("\n")}`
1655
- );
1656
- }
1657
- const targetEntrypoints = entrypoints.filter((entry) => {
1658
- const { include, exclude } = entry.options;
1659
- if (include?.length && exclude?.length) {
1660
- wxt.logger.warn(
1661
- `The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`
1662
- );
1663
- return false;
1664
- }
1665
- if (exclude?.length && !include?.length) {
1666
- return !exclude.includes(wxt.config.browser);
1667
- }
1668
- if (include?.length && !exclude?.length) {
1669
- return include.includes(wxt.config.browser);
1670
- }
1671
- if (skippedEntrypointNames.includes(entry.name)) {
1672
- return false;
1673
- }
1674
- return true;
1675
- });
1676
- wxt.logger.debug(`${wxt.config.browser} entrypoints:`, targetEntrypoints);
1677
- await wxt.hooks.callHook("entrypoints:resolved", wxt, targetEntrypoints);
1678
- return targetEntrypoints;
1679
- }
1680
- function preventDuplicateEntrypointNames(files) {
1681
- const namesToPaths = files.reduce(
1682
- (map, { name, inputPath }) => {
1683
- map[name] ??= [];
1684
- map[name].push(inputPath);
1685
- return map;
1686
- },
1687
- {}
1688
- );
1689
- const errorLines = Object.entries(namesToPaths).reduce(
1690
- (lines, [name, absolutePaths]) => {
1691
- if (absolutePaths.length > 1) {
1692
- lines.push(`- ${name}`);
1693
- absolutePaths.forEach((absolutePath) => {
1694
- lines.push(` - ${relative3(wxt.config.root, absolutePath)}`);
1695
- });
1696
- }
1697
- return lines;
1698
- },
1699
- []
1700
- );
1701
- if (errorLines.length > 0) {
1702
- const errorContent = errorLines.join("\n");
1703
- throw Error(
1704
- `Multiple entrypoints with the same name detected, only one entrypoint for each name is allowed.
1705
-
1706
- ${errorContent}`
1707
- );
1708
- }
1709
- }
1710
- function preventNoEntrypoints(files) {
1711
- if (files.length === 0) {
1712
- throw Error(`No entrypoints found in ${wxt.config.entrypointsDir}`);
1713
- }
1714
- }
1715
- async function getPopupEntrypoint(info) {
1716
- const options = await getHtmlEntrypointOptions(
1717
- info,
1718
- {
1719
- browserStyle: "browse_style",
1720
- exclude: "exclude",
1721
- include: "include",
1722
- defaultIcon: "default_icon",
1723
- defaultTitle: "default_title",
1724
- mv2Key: "type"
1725
- },
1726
- {
1727
- defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
1728
- },
1729
- {
1730
- defaultTitle: (content) => content,
1731
- mv2Key: (content) => content === "page_action" ? "page_action" : "browser_action"
1732
- }
1733
- );
1734
- return {
1735
- type: "popup",
1736
- name: "popup",
1737
- options: resolvePerBrowserOptions(options, wxt.config.browser),
1738
- inputPath: info.inputPath,
1739
- outputDir: wxt.config.outDir,
1740
- skipped: info.skipped
1741
- };
1742
- }
1743
- async function getOptionsEntrypoint(info) {
1744
- const options = await getHtmlEntrypointOptions(
1745
- info,
1746
- {
1747
- browserStyle: "browse_style",
1748
- chromeStyle: "chrome_style",
1749
- exclude: "exclude",
1750
- include: "include",
1751
- openInTab: "open_in_tab"
1752
- }
1753
- );
1754
- return {
1755
- type: "options",
1756
- name: "options",
1757
- options: resolvePerBrowserOptions(options, wxt.config.browser),
1758
- inputPath: info.inputPath,
1759
- outputDir: wxt.config.outDir,
1760
- skipped: info.skipped
1761
- };
1762
- }
1763
- async function getUnlistedPageEntrypoint(info) {
1764
- const options = await getHtmlEntrypointOptions(info, {
1765
- exclude: "exclude",
1766
- include: "include"
1767
- });
1768
- return {
1769
- type: "unlisted-page",
1770
- name: info.name,
1771
- inputPath: info.inputPath,
1772
- outputDir: wxt.config.outDir,
1773
- options,
1774
- skipped: info.skipped
1775
- };
1776
- }
1777
- async function getUnlistedScriptEntrypoint({
1778
- inputPath,
1779
- name,
1780
- skipped
1781
- }) {
1782
- const defaultExport = await wxt.builder.importEntrypoint(inputPath);
1783
- if (defaultExport == null) {
1784
- throw Error(
1785
- `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
1786
- );
1787
- }
1788
- const { main: _, ...options } = defaultExport;
1789
- return {
1790
- type: "unlisted-script",
1791
- name,
1792
- inputPath,
1793
- outputDir: wxt.config.outDir,
1794
- options: resolvePerBrowserOptions(options, wxt.config.browser),
1795
- skipped
1796
- };
1797
- }
1798
- async function getBackgroundEntrypoint({
1799
- inputPath,
1800
- name,
1801
- skipped
1802
- }) {
1803
- let options = {};
1804
- if (inputPath !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
1805
- const defaultExport = await wxt.builder.importEntrypoint(inputPath);
1806
- if (defaultExport == null) {
1807
- throw Error(
1808
- `${name}: Default export not found, did you forget to call "export default defineBackground(...)"?`
1809
- );
1810
- }
1811
- const { main: _, ...moduleOptions } = defaultExport;
1812
- options = moduleOptions;
1813
- }
1814
- if (wxt.config.manifestVersion !== 3) {
1815
- delete options.type;
1816
- }
1817
- return {
1818
- type: "background",
1819
- name,
1820
- inputPath,
1821
- outputDir: wxt.config.outDir,
1822
- options: resolvePerBrowserOptions(options, wxt.config.browser),
1823
- skipped
1824
- };
1825
- }
1826
- async function getContentScriptEntrypoint({
1827
- inputPath,
1828
- name,
1829
- skipped
1830
- }) {
1831
- const defaultExport = await wxt.builder.importEntrypoint(inputPath);
1832
- if (defaultExport == null) {
1833
- throw Error(
1834
- `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
1835
- );
1836
- }
1837
- const { main: _, ...options } = defaultExport;
1838
- if (options == null) {
1839
- throw Error(
1840
- `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
1841
- );
1842
- }
1843
- return {
1844
- type: "content-script",
1845
- name,
1846
- inputPath,
1847
- outputDir: resolve8(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
1848
- options: resolvePerBrowserOptions(options, wxt.config.browser),
1849
- skipped
1850
- };
1851
- }
1852
- async function getSidepanelEntrypoint(info) {
1853
- const options = await getHtmlEntrypointOptions(
1854
- info,
1855
- {
1856
- browserStyle: "browse_style",
1857
- exclude: "exclude",
1858
- include: "include",
1859
- defaultIcon: "default_icon",
1860
- defaultTitle: "default_title",
1861
- openAtInstall: "open_at_install"
1862
- },
1863
- {
1864
- defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
1865
- },
1866
- {
1867
- defaultTitle: (content) => content
1868
- }
1869
- );
1870
- return {
1871
- type: "sidepanel",
1872
- name: info.name,
1873
- options: resolvePerBrowserOptions(options, wxt.config.browser),
1874
- inputPath: info.inputPath,
1875
- outputDir: wxt.config.outDir,
1876
- skipped: info.skipped
1877
- };
1878
- }
1879
- async function getHtmlEntrypointOptions(info, keyMap, queries, parsers) {
1880
- const content = await fs5.readFile(info.inputPath, "utf-8");
1881
- const { document } = parseHTML3(content);
1882
- const options = {};
1883
- const defaultQuery = (manifestKey) => document.querySelector(`meta[name='manifest.${manifestKey}']`)?.getAttribute("content");
1884
- Object.entries(keyMap).forEach(([_key, manifestKey]) => {
1885
- const key = _key;
1886
- const content2 = queries?.[key] ? queries[key](document, manifestKey) : defaultQuery(manifestKey);
1887
- if (content2) {
1888
- try {
1889
- options[key] = (parsers?.[key] ?? JSON5.parse)(content2);
1890
- } catch (err) {
1891
- wxt.logger.fatal(
1892
- `Failed to parse meta tag content. Usually this means you have invalid JSON5 content (content=${content2})`,
1893
- err
1894
- );
1895
- }
1896
- }
1897
- });
1898
- return options;
1899
- }
1900
- var PATH_GLOB_TO_TYPE_MAP = {
1901
- "sandbox.html": "sandbox",
1902
- "sandbox/index.html": "sandbox",
1903
- "*.sandbox.html": "sandbox",
1904
- "*.sandbox/index.html": "sandbox",
1905
- "bookmarks.html": "bookmarks",
1906
- "bookmarks/index.html": "bookmarks",
1907
- "history.html": "history",
1908
- "history/index.html": "history",
1909
- "newtab.html": "newtab",
1910
- "newtab/index.html": "newtab",
1911
- "sidepanel.html": "sidepanel",
1912
- "sidepanel/index.html": "sidepanel",
1913
- "*.sidepanel.html": "sidepanel",
1914
- "*.sidepanel/index.html": "sidepanel",
1915
- "devtools.html": "devtools",
1916
- "devtools/index.html": "devtools",
1917
- "background.[jt]s": "background",
1918
- "background/index.[jt]s": "background",
1919
- [VIRTUAL_NOOP_BACKGROUND_MODULE_ID]: "background",
1920
- "content.[jt]s?(x)": "content-script",
1921
- "content/index.[jt]s?(x)": "content-script",
1922
- "*.content.[jt]s?(x)": "content-script",
1923
- "*.content/index.[jt]s?(x)": "content-script",
1924
- [`content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
1925
- [`*.content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
1926
- [`content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
1927
- [`*.content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
1928
- "popup.html": "popup",
1929
- "popup/index.html": "popup",
1930
- "options.html": "options",
1931
- "options/index.html": "options",
1932
- "*.html": "unlisted-page",
1933
- "*/index.html": "unlisted-page",
1934
- "*.[jt]s?(x)": "unlisted-script",
1935
- "*/index.[jt]s?(x)": "unlisted-script",
1936
- [`*.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
1937
- [`*/index.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style"
1938
- };
1939
- var CONTENT_SCRIPT_OUT_DIR = "content-scripts";
1940
-
1941
- // src/core/utils/building/generate-wxt-dir.ts
1942
- import fs6 from "fs-extra";
1943
- import { dirname as dirname4, relative as relative4, resolve as resolve9 } from "node:path";
1944
- import path5 from "node:path";
1945
-
1946
- // src/core/utils/i18n.ts
1947
- var predefinedMessages = {
1948
- "@@extension_id": {
1949
- message: "<browser.runtime.id>",
1950
- 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."
1951
- },
1952
- "@@ui_locale": {
1953
- message: "<browser.i18n.getUiLocale()>",
1954
- description: ""
1955
- },
1956
- "@@bidi_dir": {
1957
- message: "<ltr|rtl>",
1958
- 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.'
1959
- },
1960
- "@@bidi_reversed_dir": {
1961
- message: "<rtl|ltr>",
1962
- description: `If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".`
1963
- },
1964
- "@@bidi_start_edge": {
1965
- message: "<left|right>",
1966
- description: `If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".`
1967
- },
1968
- "@@bidi_end_edge": {
1969
- message: "<right|left>",
1970
- description: `If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".`
1971
- }
1972
- };
1973
- function parseI18nMessages(messagesJson) {
1974
- return Object.entries({
1975
- ...predefinedMessages,
1976
- ...messagesJson
1977
- }).map(([name, details]) => ({
1978
- name,
1979
- ...details
1980
- }));
1981
- }
1982
-
1983
- // src/core/utils/building/generate-wxt-dir.ts
1984
- async function generateTypesDir(entrypoints) {
1985
- await fs6.ensureDir(wxt.config.typesDir);
1986
- const entries = [
1987
- // Hard-coded entries
1988
- { module: "wxt/vite-builder-env" }
1989
- ];
1990
- wxt.config.userModules.forEach((module) => {
1991
- if (module.type === "node_module") entries.push({ module: module.id });
1992
- });
1993
- entries.push(await getPathsDeclarationEntry(entrypoints));
1994
- entries.push(await getI18nDeclarationEntry());
1995
- entries.push(await getGlobalsDeclarationEntry());
1996
- entries.push(await getTsConfigEntry());
1997
- await wxt.hooks.callHook("prepare:types", wxt, entries);
1998
- entries.push(getMainDeclarationEntry(entries));
1999
- const absoluteFileEntries = entries.filter((entry) => "path" in entry).map((entry) => ({
2000
- ...entry,
2001
- path: resolve9(wxt.config.wxtDir, entry.path)
2002
- }));
2003
- await Promise.all(
2004
- absoluteFileEntries.map(async (file) => {
2005
- await fs6.ensureDir(dirname4(file.path));
2006
- await writeFileIfDifferent(file.path, file.text);
2007
- })
2008
- );
2009
- }
2010
- async function getPathsDeclarationEntry(entrypoints) {
2011
- const unions = entrypoints.map(
2012
- (entry) => getEntrypointBundlePath(
2013
- entry,
2014
- wxt.config.outDir,
2015
- isHtmlEntrypoint(entry) ? ".html" : ".js"
2016
- )
2017
- ).concat(await getPublicFiles()).map(normalizePath).map((path11) => ` | "/${path11}"`).sort().join("\n");
2018
- const template = `// Generated by wxt
2019
- import "wxt/browser";
2020
-
2021
- declare module "wxt/browser" {
2022
- export type PublicPath =
2023
- {{ union }}
2024
- type HtmlPublicPath = Extract<PublicPath, \`\${string}.html\`>
2025
- export interface WxtRuntime extends Runtime.Static {
2026
- getURL(path: PublicPath): string;
2027
- getURL(path: \`\${HtmlPublicPath}\${string}\`): string;
2028
- }
2029
- }
2030
- `;
2031
- return {
2032
- path: "types/paths.d.ts",
2033
- text: template.replace("{{ union }}", unions || " | never"),
2034
- tsReference: true
2035
- };
2036
- }
2037
- async function getI18nDeclarationEntry() {
2038
- const defaultLocale = wxt.config.manifest.default_locale;
2039
- const template = `// Generated by wxt
2040
- import "wxt/browser";
2041
-
2042
- declare module "wxt/browser" {
2043
- /**
2044
- * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
2045
- */
2046
- interface GetMessageOptions {
2047
- /**
2048
- * See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
2049
- */
2050
- escapeLt?: boolean
2051
- }
2052
-
2053
- export interface WxtI18n extends I18n.Static {
2054
- {{ overrides }}
2055
- }
2056
- }
2057
- `;
2058
- let messages;
2059
- if (defaultLocale) {
2060
- const defaultLocalePath = path5.resolve(
2061
- wxt.config.publicDir,
2062
- "_locales",
2063
- defaultLocale,
2064
- "messages.json"
2065
- );
2066
- const content = JSON.parse(await fs6.readFile(defaultLocalePath, "utf-8"));
2067
- messages = parseI18nMessages(content);
2068
- } else {
2069
- messages = parseI18nMessages({});
2070
- }
2071
- const overrides = messages.map((message) => {
2072
- return ` /**
2073
- * ${message.description || "No message description."}
2074
- *
2075
- * "${message.message}"
2076
- */
2077
- getMessage(
2078
- messageName: "${message.name}",
2079
- substitutions?: string | string[],
2080
- options?: GetMessageOptions,
2081
- ): string;`;
2082
- });
2083
- return {
2084
- path: "types/i18n.d.ts",
2085
- text: template.replace("{{ overrides }}", overrides.join("\n")),
2086
- tsReference: true
2087
- };
2088
- }
2089
- async function getGlobalsDeclarationEntry() {
2090
- const globals2 = [...getGlobals(wxt.config), ...getEntrypointGlobals("")];
2091
- return {
2092
- path: "types/globals.d.ts",
2093
- text: [
2094
- "// Generated by wxt",
2095
- "export {}",
2096
- "interface ImportMetaEnv {",
2097
- ...globals2.map((global) => ` readonly ${global.name}: ${global.type};`),
2098
- "}",
2099
- "interface ImportMeta {",
2100
- " readonly env: ImportMetaEnv",
2101
- "}",
2102
- ""
2103
- ].join("\n"),
2104
- tsReference: true
2105
- };
2106
- }
2107
- function getMainDeclarationEntry(references) {
2108
- const lines = ["// Generated by wxt"];
2109
- references.forEach((ref) => {
2110
- if ("module" in ref) {
2111
- return lines.push(`/// <reference types="${ref.module}" />`);
2112
- } else if (ref.tsReference) {
2113
- const absolutePath = resolve9(wxt.config.wxtDir, ref.path);
2114
- const relativePath = relative4(wxt.config.wxtDir, absolutePath);
2115
- lines.push(`/// <reference types="./${normalizePath(relativePath)}" />`);
2116
- }
2117
- });
2118
- return {
2119
- path: "wxt.d.ts",
2120
- text: lines.join("\n") + "\n"
2121
- };
2122
- }
2123
- async function getTsConfigEntry() {
2124
- const dir = wxt.config.wxtDir;
2125
- const getTsconfigPath = (path11) => normalizePath(relative4(dir, path11));
2126
- const paths = Object.entries(wxt.config.alias).flatMap(([alias, absolutePath]) => {
2127
- const aliasPath = getTsconfigPath(absolutePath);
2128
- return [
2129
- ` "${alias}": ["${aliasPath}"]`,
2130
- ` "${alias}/*": ["${aliasPath}/*"]`
2131
- ];
2132
- }).join(",\n");
2133
- const text = `{
2134
- "compilerOptions": {
2135
- "target": "ESNext",
2136
- "module": "ESNext",
2137
- "moduleResolution": "Bundler",
2138
- "noEmit": true,
2139
- "esModuleInterop": true,
2140
- "forceConsistentCasingInFileNames": true,
2141
- "resolveJsonModule": true,
2142
- "strict": true,
2143
- "skipLibCheck": true,
2144
- "paths": {
2145
- ${paths}
2146
- }
2147
- },
2148
- "include": [
2149
- "${getTsconfigPath(wxt.config.root)}/**/*",
2150
- "./wxt.d.ts"
2151
- ],
2152
- "exclude": ["${getTsconfigPath(wxt.config.outBaseDir)}"]
2153
- }`;
2154
- return {
2155
- path: "tsconfig.json",
2156
- text
2157
- };
2158
- }
2159
-
2160
- // src/core/utils/building/resolve-config.ts
2161
- import { loadConfig } from "c12";
2162
- import path6 from "node:path";
2163
-
2164
- // src/core/utils/cache.ts
2165
- import fs7, { ensureDir as ensureDir3 } from "fs-extra";
2166
- import { dirname as dirname5, resolve as resolve10 } from "path";
2167
- function createFsCache(wxtDir) {
2168
- const getPath = (key) => resolve10(wxtDir, "cache", encodeURIComponent(key));
2169
- return {
2170
- async set(key, value) {
2171
- const path11 = getPath(key);
2172
- await ensureDir3(dirname5(path11));
2173
- await writeFileIfDifferent(path11, value);
2174
- },
2175
- async get(key) {
2176
- const path11 = getPath(key);
2177
- try {
2178
- return await fs7.readFile(path11, "utf-8");
2179
- } catch {
2180
- return void 0;
2181
- }
2182
- }
2183
- };
2184
- }
2185
-
2186
- // src/core/utils/building/resolve-config.ts
2187
- import defu from "defu";
2188
- import fs8 from "fs-extra";
2189
- import glob4 from "fast-glob";
2190
-
2191
- // src/core/utils/eslint.ts
2192
- async function getEslintVersion() {
2193
- try {
2194
- const require2 = (await import("node:module")).default.createRequire(
2195
- import.meta.url
2196
- );
2197
- const { ESLint } = require2("eslint");
2198
- return ESLint.version?.split(".") ?? [];
2199
- } catch (error) {
2200
- return [];
2201
- }
2202
- }
2203
-
2204
- // src/core/utils/building/resolve-config.ts
2205
- async function resolveConfig(inlineConfig, command) {
2206
- let userConfig = {};
2207
- let userConfigMetadata;
2208
- if (inlineConfig.configFile !== false) {
2209
- const { config: loadedConfig, ...metadata } = await loadConfig({
2210
- configFile: inlineConfig.configFile,
2211
- name: "wxt",
2212
- cwd: inlineConfig.root ?? process.cwd(),
2213
- rcFile: false,
2214
- jitiOptions: {
2215
- esmResolve: true
2216
- }
2217
- });
2218
- userConfig = loadedConfig ?? {};
2219
- userConfigMetadata = metadata;
2220
- }
2221
- const mergedConfig = await mergeInlineConfig(inlineConfig, userConfig);
2222
- const debug = mergedConfig.debug ?? false;
2223
- const logger = mergedConfig.logger ?? consola;
2224
- if (debug) logger.level = LogLevels.debug;
2225
- const browser = mergedConfig.browser ?? "chrome";
2226
- const manifestVersion = mergedConfig.manifestVersion ?? (browser === "firefox" || browser === "safari" ? 2 : 3);
2227
- const mode = mergedConfig.mode ?? COMMAND_MODES[command];
2228
- const env = { browser, command, manifestVersion, mode };
2229
- const root = path6.resolve(
2230
- inlineConfig.root ?? userConfig.root ?? process.cwd()
2231
- );
2232
- const wxtDir = path6.resolve(root, ".wxt");
2233
- const wxtModuleDir = await resolveWxtModuleDir();
2234
- const srcDir = path6.resolve(root, mergedConfig.srcDir ?? root);
2235
- const entrypointsDir = path6.resolve(
2236
- srcDir,
2237
- mergedConfig.entrypointsDir ?? "entrypoints"
2238
- );
2239
- const modulesDir = path6.resolve(srcDir, mergedConfig.modulesDir ?? "modules");
2240
- if (await isDirMissing(entrypointsDir)) {
2241
- logMissingDir(logger, "Entrypoints", entrypointsDir);
2242
- }
2243
- const filterEntrypoints = !!mergedConfig.filterEntrypoints?.length ? new Set(mergedConfig.filterEntrypoints) : void 0;
2244
- const publicDir = path6.resolve(srcDir, mergedConfig.publicDir ?? "public");
2245
- if (await isDirMissing(publicDir)) {
2246
- logMissingDir(logger, "Public", publicDir);
2247
- }
2248
- const typesDir = path6.resolve(wxtDir, "types");
2249
- const outBaseDir = path6.resolve(root, mergedConfig.outDir ?? ".output");
2250
- const outDir = path6.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
2251
- const reloadCommand = mergedConfig.dev?.reloadCommand ?? "Alt+R";
2252
- const runnerConfig = await loadConfig({
2253
- name: "web-ext",
2254
- cwd: root,
2255
- globalRc: true,
2256
- rcFile: ".webextrc",
2257
- overrides: inlineConfig.runner,
2258
- defaults: userConfig.runner
2259
- });
2260
- const alias = Object.fromEntries(
2261
- Object.entries({
2262
- ...mergedConfig.alias,
2263
- "@": srcDir,
2264
- "~": srcDir,
2265
- "@@": root,
2266
- "~~": root
2267
- }).map(([key, value]) => [key, path6.resolve(root, value)])
2268
- );
2269
- let devServerConfig;
2270
- if (command === "serve") {
2271
- let port = mergedConfig.dev?.server?.port;
2272
- if (port == null || !isFinite(port)) {
2273
- const { default: getPort, portNumbers } = await import("get-port");
2274
- port = await getPort({ port: portNumbers(3e3, 3010) });
2275
- }
2276
- devServerConfig = {
2277
- port,
2278
- hostname: mergedConfig.dev?.server?.hostname ?? "localhost"
2279
- };
2280
- }
2281
- const userModules = await resolveWxtUserModules(
2282
- modulesDir,
2283
- mergedConfig.modules
2284
- );
2285
- const moduleOptions = userModules.reduce(
2286
- (map, module) => {
2287
- if (module.configKey) {
2288
- map[module.configKey] = // @ts-expect-error
2289
- mergedConfig[module.configKey];
2290
- }
2291
- return map;
2292
- },
2293
- {}
2294
- );
2295
- return {
2296
- browser,
2297
- command,
2298
- debug,
2299
- entrypointsDir,
2300
- modulesDir,
2301
- filterEntrypoints,
2302
- env,
2303
- fsCache: createFsCache(wxtDir),
2304
- imports: await getUnimportOptions(wxtDir, srcDir, logger, mergedConfig),
2305
- logger,
2306
- manifest: await resolveManifestConfig(env, mergedConfig.manifest),
2307
- manifestVersion,
2308
- mode,
2309
- outBaseDir,
2310
- outDir,
2311
- publicDir,
2312
- wxtModuleDir,
2313
- root,
2314
- runnerConfig,
2315
- srcDir,
2316
- typesDir,
2317
- wxtDir,
2318
- zip: resolveZipConfig(root, outBaseDir, mergedConfig),
2319
- transformManifest: mergedConfig.transformManifest,
2320
- analysis: resolveAnalysisConfig(root, mergedConfig),
2321
- userConfigMetadata: userConfigMetadata ?? {},
2322
- alias,
2323
- experimental: defu(mergedConfig.experimental, {
2324
- includeBrowserPolyfill: true,
2325
- entrypointImporter: "jiti"
2326
- }),
2327
- dev: {
2328
- server: devServerConfig,
2329
- reloadCommand
2330
- },
2331
- hooks: mergedConfig.hooks ?? {},
2332
- vite: mergedConfig.vite ?? (() => ({})),
2333
- builtinModules,
2334
- userModules,
2335
- plugins: [],
2336
- ...moduleOptions
2337
- };
2338
- }
2339
- async function resolveManifestConfig(env, manifest) {
2340
- return await (typeof manifest === "function" ? manifest(env) : manifest ?? {});
2341
- }
2342
- async function mergeInlineConfig(inlineConfig, userConfig) {
2343
- const imports = inlineConfig.imports === false || userConfig.imports === false ? false : userConfig.imports == null && inlineConfig.imports == null ? void 0 : defu(inlineConfig.imports ?? {}, userConfig.imports ?? {});
2344
- const manifest = async (env) => {
2345
- const user = await resolveManifestConfig(env, userConfig.manifest);
2346
- const inline = await resolveManifestConfig(env, inlineConfig.manifest);
2347
- return defu(inline, user);
2348
- };
2349
- const transformManifest = (manifest2) => {
2350
- userConfig.transformManifest?.(manifest2);
2351
- inlineConfig.transformManifest?.(manifest2);
2352
- };
2353
- const merged = defu(inlineConfig, userConfig);
2354
- const builderConfig = await mergeBuilderConfig(
2355
- merged.logger ?? consola,
2356
- inlineConfig,
2357
- userConfig
2358
- );
2359
- return {
2360
- ...merged,
2361
- // Custom merge values
2362
- transformManifest,
2363
- imports,
2364
- manifest,
2365
- ...builderConfig
2366
- };
2367
- }
2368
- function resolveZipConfig(root, outBaseDir, mergedConfig) {
2369
- const downloadedPackagesDir = path6.resolve(root, ".wxt/local_modules");
2370
- return {
2371
- name: void 0,
2372
- sourcesTemplate: "{{name}}-{{version}}-sources.zip",
2373
- artifactTemplate: "{{name}}-{{version}}-{{browser}}.zip",
2374
- sourcesRoot: root,
2375
- includeSources: [],
2376
- compressionLevel: 9,
2377
- ...mergedConfig.zip,
2378
- excludeSources: [
2379
- "**/node_modules",
2380
- // WXT files
2381
- "**/web-ext.config.ts",
2382
- // Hidden files
2383
- "**/.*",
2384
- // Tests
2385
- "**/__tests__/**",
2386
- "**/*.+(test|spec).?(c|m)+(j|t)s?(x)",
2387
- // Output directory
2388
- `${path6.relative(root, outBaseDir)}/**`,
2389
- // From user
2390
- ...mergedConfig.zip?.excludeSources ?? []
2391
- ],
2392
- downloadPackages: mergedConfig.zip?.downloadPackages ?? [],
2393
- downloadedPackagesDir
2394
- };
2395
- }
2396
- function resolveAnalysisConfig(root, mergedConfig) {
2397
- const analysisOutputFile = path6.resolve(
2398
- root,
2399
- mergedConfig.analysis?.outputFile ?? "stats.html"
2400
- );
2401
- const analysisOutputDir = path6.dirname(analysisOutputFile);
2402
- const analysisOutputName = path6.parse(analysisOutputFile).name;
2403
- return {
2404
- enabled: mergedConfig.analysis?.enabled ?? false,
2405
- open: mergedConfig.analysis?.open ?? false,
2406
- template: mergedConfig.analysis?.template ?? "treemap",
2407
- outputFile: analysisOutputFile,
2408
- outputDir: analysisOutputDir,
2409
- outputName: analysisOutputName,
2410
- keepArtifacts: mergedConfig.analysis?.keepArtifacts ?? false
2411
- };
2412
- }
2413
- async function getUnimportOptions(wxtDir, srcDir, logger, config) {
2414
- if (config.imports === false) return false;
2415
- const defaultOptions = {
2416
- debugLog: logger.debug,
2417
- imports: [
2418
- { name: "defineConfig", from: "wxt" },
2419
- { name: "fakeBrowser", from: "wxt/testing" }
2420
- ],
2421
- presets: [
2422
- { package: "wxt/client" },
2423
- { package: "wxt/browser" },
2424
- { package: "wxt/sandbox" },
2425
- { package: "wxt/storage" }
2426
- ],
2427
- warn: logger.warn,
2428
- dirs: ["components", "composables", "hooks", "utils"],
2429
- dirsScanOptions: {
2430
- cwd: srcDir
2431
- },
2432
- eslintrc: await getUnimportEslintOptions(wxtDir, config.imports?.eslintrc)
2433
- };
2434
- return defu(
2435
- config.imports ?? {},
2436
- defaultOptions
2437
- );
2438
- }
2439
- async function getUnimportEslintOptions(wxtDir, options) {
2440
- const rawEslintEnabled = options?.enabled ?? "auto";
2441
- let eslintEnabled;
2442
- switch (rawEslintEnabled) {
2443
- case "auto":
2444
- const version2 = await getEslintVersion();
2445
- let major = parseInt(version2[0]);
2446
- if (major <= 8) eslintEnabled = 8;
2447
- else if (major >= 9) eslintEnabled = 9;
2448
- else eslintEnabled = 8;
2449
- break;
2450
- case true:
2451
- eslintEnabled = 8;
2452
- break;
2453
- default:
2454
- eslintEnabled = rawEslintEnabled;
2455
- }
2456
- return {
2457
- enabled: eslintEnabled,
2458
- filePath: path6.resolve(
2459
- wxtDir,
2460
- eslintEnabled === 9 ? "eslint-auto-imports.mjs" : "eslintrc-auto-import.json"
2461
- ),
2462
- globalsPropValue: true
2463
- };
2464
- }
2465
- async function resolveWxtModuleDir() {
2466
- const requireResolve = __require?.resolve ?? (await import("node:module")).default.createRequire(import.meta.url).resolve;
2467
- return path6.resolve(requireResolve("wxt"), "../..");
2468
- }
2469
- async function isDirMissing(dir) {
2470
- return !await fs8.exists(dir);
2471
- }
2472
- function logMissingDir(logger, name, expected) {
2473
- logger.warn(
2474
- `${name} directory not found: ./${normalizePath(
2475
- path6.relative(process.cwd(), expected)
2476
- )}`
2477
- );
2478
- }
2479
- var COMMAND_MODES = {
2480
- build: "production",
2481
- serve: "development"
2482
- };
2483
- async function mergeBuilderConfig(logger, inlineConfig, userConfig) {
2484
- const vite2 = await import("vite").catch((err) => {
2485
- logger.debug("Failed to import vite:", err);
2486
- });
2487
- if (vite2) {
2488
- return {
2489
- vite: async (env) => {
2490
- const resolvedInlineConfig = await inlineConfig.vite?.(env) ?? {};
2491
- const resolvedUserConfig = await userConfig.vite?.(env) ?? {};
2492
- return vite2.mergeConfig(resolvedUserConfig, resolvedInlineConfig);
2493
- }
2494
- };
2495
- }
2496
- throw Error("Builder not found. Make sure vite is installed.");
2497
- }
2498
- async function resolveWxtUserModules(modulesDir, modules = []) {
2499
- const npmModules = await Promise.all(
2500
- modules.map(async (moduleId) => {
2501
- const mod = await import(
2502
- /* @vite-ignore */
2503
- moduleId
2504
- );
2505
- if (mod.default == null) {
2506
- throw Error("Module missing default export: " + moduleId);
2507
- }
2508
- return {
2509
- ...mod.default,
2510
- type: "node_module",
2511
- id: moduleId
2512
- };
2513
- })
2514
- );
2515
- const localModulePaths = await glob4(["*.[tj]s", "*/index.[tj]s"], {
2516
- cwd: modulesDir,
2517
- onlyFiles: true
2518
- }).catch(() => []);
2519
- const localModules = await Promise.all(
2520
- localModulePaths.map(async (file) => {
2521
- const absolutePath = normalizePath(path6.resolve(modulesDir, file));
2522
- const { config } = await loadConfig({
2523
- configFile: absolutePath,
2524
- globalRc: false,
2525
- rcFile: false,
2526
- packageJson: false,
2527
- envName: false,
2528
- dotenv: false
2529
- });
2530
- if (config == null)
2531
- throw Error(
2532
- `No config found for ${file}. Did you forget to add a default export?`
2533
- );
2534
- config.name ??= file;
2535
- return {
2536
- ...config,
2537
- type: "local",
2538
- id: absolutePath
2539
- };
2540
- })
2541
- );
2542
- return [...npmModules, ...localModules];
2543
- }
2544
-
2545
- // src/core/utils/building/group-entrypoints.ts
2546
- function groupEntrypoints(entrypoints) {
2547
- const groupIndexMap = {};
2548
- const groups = [];
2549
- for (const entry of entrypoints) {
2550
- let group = ENTRY_TYPE_TO_GROUP_MAP[entry.type];
2551
- if (entry.type === "background" && entry.options.type === "module") {
2552
- group = "esm";
2553
- }
2554
- if (group === "individual") {
2555
- groups.push(entry);
2556
- } else {
2557
- let groupIndex = groupIndexMap[group];
2558
- if (groupIndex == null) {
2559
- groupIndex = groups.push([]) - 1;
2560
- groupIndexMap[group] = groupIndex;
2561
- }
2562
- groups[groupIndex].push(entry);
2563
- }
2564
- }
2565
- return groups;
2566
- }
2567
- var ENTRY_TYPE_TO_GROUP_MAP = {
2568
- sandbox: "sandboxed-esm",
2569
- popup: "esm",
2570
- newtab: "esm",
2571
- history: "esm",
2572
- options: "esm",
2573
- devtools: "esm",
2574
- bookmarks: "esm",
2575
- sidepanel: "esm",
2576
- "unlisted-page": "esm",
2577
- background: "individual",
2578
- "content-script": "individual",
2579
- "unlisted-script": "individual",
2580
- "unlisted-style": "individual",
2581
- "content-script-style": "individual"
2582
- };
2583
-
2584
- // src/core/utils/building/import-entrypoint.ts
2585
- import createJITI from "jiti";
2586
- import { createUnimport as createUnimport2 } from "unimport";
2587
- import fs9 from "fs-extra";
2588
- import { relative as relative5, resolve as resolve11 } from "node:path";
2589
- import { transformSync } from "esbuild";
2590
- import { fileURLToPath } from "node:url";
2591
- async function importEntrypointFile(path11) {
2592
- wxt.logger.debug("Loading file metadata:", path11);
2593
- const normalPath = normalizePath(path11);
2594
- const unimport = createUnimport2({
2595
- ...wxt.config.imports,
2596
- // Only allow specific imports, not all from the project
2597
- dirs: []
2598
- });
2599
- await unimport.init();
2600
- const text = await fs9.readFile(path11, "utf-8");
2601
- const textNoImports = removeProjectImportStatements(text);
2602
- const { code } = await unimport.injectImports(textNoImports);
2603
- wxt.logger.debug(
2604
- ["Text:", text, "No imports:", textNoImports, "Code:", code].join("\n")
2605
- );
2606
- const jiti = createJITI(
2607
- typeof __filename !== "undefined" ? __filename : fileURLToPath(import.meta.url),
2608
- {
2609
- cache: false,
2610
- debug: wxt.config.debug,
2611
- esmResolve: true,
2612
- alias: {
2613
- "webextension-polyfill": resolve11(
2614
- wxt.config.wxtModuleDir,
2615
- "dist/virtual/mock-browser.js"
2616
- ),
2617
- // TODO: Resolve this virtual module to some file with
2618
- // `export default {}` instead of this hack of using another file with
2619
- // a default export.
2620
- "virtual:app-config": resolve11(
2621
- wxt.config.wxtModuleDir,
2622
- "dist/virtual/mock-browser.js"
2623
- )
2624
- },
2625
- // Continue using node to load TS files even if `bun run --bun` is detected. Jiti does not
2626
- // respect the custom transform function when using it's native bun option.
2627
- experimentalBun: false,
2628
- // List of extensions to transform with esbuild
2629
- extensions: [
2630
- ".ts",
2631
- ".cts",
2632
- ".mts",
2633
- ".tsx",
2634
- ".js",
2635
- ".cjs",
2636
- ".mjs",
2637
- ".jsx"
2638
- ],
2639
- transform(opts) {
2640
- const isEntrypoint = opts.filename === normalPath;
2641
- return transformSync(
2642
- // Use modified source code for entrypoints
2643
- isEntrypoint ? code : opts.source,
2644
- getEsbuildOptions(opts)
2645
- );
2646
- }
2647
- }
2648
- );
2649
- try {
2650
- const res = await jiti(path11);
2651
- return res.default;
2652
- } catch (err) {
2653
- const filePath = relative5(wxt.config.root, path11);
2654
- if (err instanceof ReferenceError) {
2655
- const variableName = err.message.replace(" is not defined", "");
2656
- throw Error(
2657
- `${filePath}: Cannot use imported variable "${variableName}" outside the main function. See https://wxt.dev/guide/go-further/entrypoint-side-effects.html`,
2658
- { cause: err }
2659
- );
2660
- } else {
2661
- wxt.logger.error(err);
2662
- throw Error(`Failed to load entrypoint: ${filePath}`, { cause: err });
2663
- }
2664
- }
2665
- }
2666
- function getEsbuildOptions(opts) {
2667
- const isJsx = opts.filename?.endsWith("x");
2668
- return {
2669
- format: "cjs",
2670
- loader: isJsx ? "tsx" : "ts",
2671
- ...isJsx ? {
2672
- // `h` and `Fragment` are undefined, but that's OK because JSX is never evaluated while
2673
- // grabbing the entrypoint's options.
2674
- jsxFactory: "h",
2675
- jsxFragment: "Fragment"
2676
- } : void 0
2677
- };
2678
- }
2679
-
2680
- // src/core/utils/building/internal-build.ts
2681
- import pc5 from "picocolors";
2682
- import fs13 from "fs-extra";
2683
-
2684
- // src/core/utils/log/printBuildSummary.ts
2685
- import { resolve as resolve12 } from "path";
2686
-
2687
- // src/core/utils/log/printFileList.ts
2688
- import path7 from "node:path";
2689
- import pc3 from "picocolors";
2690
- import fs10 from "fs-extra";
2691
- import { filesize } from "filesize";
2692
-
2693
- // src/core/utils/log/printTable.ts
2694
- function printTable(log, header, rows, gap = 2) {
2695
- if (rows.length === 0) return;
2696
- const columnWidths = rows.reduce(
2697
- (widths, row) => {
2698
- for (let i = 0; i < Math.max(widths.length, row.length); i++) {
2699
- widths[i] = Math.max(row[i]?.length ?? 0, widths[i] ?? 0);
2700
- }
2701
- return widths;
2702
- },
2703
- rows[0].map((column) => column.length)
2704
- );
2705
- let str = "";
2706
- rows.forEach((row, i) => {
2707
- row.forEach((col, j) => {
2708
- str += col.padEnd(columnWidths[j], " ");
2709
- if (j !== row.length - 1) str += "".padEnd(gap, " ");
2710
- });
2711
- if (i !== rows.length - 1) str += "\n";
2712
- });
2713
- log(`${header}
2714
- ${str}`);
2715
- }
2716
-
2717
- // src/core/utils/log/printFileList.ts
2718
- async function printFileList(log, header, baseDir, files) {
2719
- let totalSize = 0;
2720
- const fileRows = await Promise.all(
2721
- files.map(async (file, i) => {
2722
- const parts = [
2723
- path7.relative(process.cwd(), baseDir) + path7.sep,
2724
- path7.relative(baseDir, file)
2725
- ];
2726
- const prefix = i === files.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
2727
- const color = getChunkColor(file);
2728
- const stats = await fs10.lstat(file);
2729
- totalSize += stats.size;
2730
- const size = String(filesize(stats.size));
2731
- return [
2732
- `${pc3.gray(prefix)} ${pc3.dim(parts[0])}${color(parts[1])}`,
2733
- pc3.dim(size)
2734
- ];
2735
- })
2736
- );
2737
- fileRows.push([`${pc3.cyan("\u03A3 Total size:")} ${String(filesize(totalSize))}`]);
2738
- printTable(log, header, fileRows);
2739
- }
2740
- var DEFAULT_COLOR = pc3.blue;
2741
- var CHUNK_COLORS = {
2742
- ".js.map": pc3.gray,
2743
- ".cjs.map": pc3.gray,
2744
- ".mjs.map": pc3.gray,
2745
- ".html": pc3.green,
2746
- ".css": pc3.magenta,
2747
- ".js": pc3.cyan,
2748
- ".cjs": pc3.cyan,
2749
- ".mjs": pc3.cyan,
2750
- ".zip": pc3.yellow
2751
- };
2752
- function getChunkColor(filename) {
2753
- return Object.entries(CHUNK_COLORS).find(([key]) => filename.endsWith(key))?.[1] ?? DEFAULT_COLOR;
2754
- }
2755
-
2756
- // src/core/utils/log/printBuildSummary.ts
2757
- async function printBuildSummary(log, header, output) {
2758
- const chunks = [
2759
- ...output.steps.flatMap((step) => step.chunks),
2760
- ...output.publicAssets
2761
- ].sort((l, r) => {
2762
- const lWeight = getChunkSortWeight(l.fileName);
2763
- const rWeight = getChunkSortWeight(r.fileName);
2764
- const diff = lWeight - rWeight;
2765
- if (diff !== 0) return diff;
2766
- return l.fileName.localeCompare(r.fileName);
2767
- });
2768
- const files = chunks.map(
2769
- (chunk) => resolve12(wxt.config.outDir, chunk.fileName)
2770
- );
2771
- await printFileList(log, header, wxt.config.outDir, files);
2772
- }
2773
- var DEFAULT_SORT_WEIGHT = 100;
2774
- var CHUNK_SORT_WEIGHTS = {
2775
- "manifest.json": 0,
2776
- ".html": 1,
2777
- ".js.map": 2,
2778
- ".js": 2,
2779
- ".css": 3
2780
- };
2781
- function getChunkSortWeight(filename) {
2782
- return Object.entries(CHUNK_SORT_WEIGHTS).find(
2783
- ([key]) => filename.endsWith(key)
2784
- )?.[1] ?? DEFAULT_SORT_WEIGHT;
2785
- }
2786
-
2787
- // src/core/utils/log/printHeader.ts
2788
- import pc4 from "picocolors";
2789
-
2790
- // package.json
2791
- var version = "0.18.13";
2792
-
2793
- // src/core/utils/log/printHeader.ts
2794
- function printHeader() {
2795
- console.log();
2796
- consola.log(`${pc4.gray("WXT")} ${pc4.gray(pc4.bold(version))}`);
2797
- }
2798
-
2799
- // src/core/utils/building/internal-build.ts
2800
- import glob5 from "fast-glob";
2801
-
2802
- // src/core/utils/manifest.ts
2803
- import fs12 from "fs-extra";
2804
- import { resolve as resolve14 } from "path";
2805
-
2806
- // src/core/utils/content-security-policy.ts
2807
- var ContentSecurityPolicy = class _ContentSecurityPolicy {
2808
- static DIRECTIVE_ORDER = {
2809
- "default-src": 0,
2810
- "script-src": 1,
2811
- "object-src": 2
2812
- };
2813
- data;
2814
- constructor(csp) {
2815
- if (csp) {
2816
- const sections = csp.split(";").map((section) => section.trim());
2817
- this.data = sections.reduce((data, section) => {
2818
- const [key, ...values] = section.split(" ").map((item) => item.trim());
2819
- if (key) data[key] = values;
2820
- return data;
2821
- }, {});
2822
- } else {
2823
- this.data = {};
2824
- }
2825
- }
2826
- /**
2827
- * Ensure a set of values are listed under a directive.
2828
- */
2829
- add(directive, ...newValues) {
2830
- const values = this.data[directive] ?? [];
2831
- newValues.forEach((newValue) => {
2832
- if (!values.includes(newValue)) values.push(newValue);
2833
- });
2834
- this.data[directive] = values;
2835
- return this;
2836
- }
2837
- toString() {
2838
- const directives = Object.entries(this.data).sort(([l], [r]) => {
2839
- const lo = _ContentSecurityPolicy.DIRECTIVE_ORDER[l] ?? 2;
2840
- const ro = _ContentSecurityPolicy.DIRECTIVE_ORDER[r] ?? 2;
2841
- return lo - ro;
2842
- });
2843
- return directives.map((entry) => entry.flat().join(" ")).join("; ") + ";";
2844
- }
2845
- };
2846
-
2847
- // src/core/utils/content-scripts.ts
2848
- function hashContentScriptOptions(options) {
2849
- const simplifiedOptions = mapWxtOptionsToContentScript(
2850
- options,
2851
- void 0,
2852
- void 0
2853
- );
2854
- Object.keys(simplifiedOptions).forEach((key) => {
2855
- if (simplifiedOptions[key] == null) delete simplifiedOptions[key];
2856
- });
2857
- const withDefaults = {
2858
- exclude_globs: [],
2859
- exclude_matches: [],
2860
- include_globs: [],
2861
- match_about_blank: false,
2862
- run_at: "document_idle",
2863
- all_frames: false,
2864
- // @ts-expect-error - not in type
2865
- match_origin_as_fallback: false,
2866
- world: "ISOLATED",
2867
- ...simplifiedOptions
2868
- };
2869
- return JSON.stringify(
2870
- Object.entries(withDefaults).map(([key, value]) => {
2871
- if (Array.isArray(value)) return [key, value.sort()];
2872
- else return [key, value];
2873
- }).sort((l, r) => l[0].localeCompare(r[0]))
2874
- );
2875
- }
2876
- function mapWxtOptionsToContentScript(options, js, css) {
2877
- return {
2878
- matches: options.matches,
2879
- all_frames: options.allFrames,
2880
- match_about_blank: options.matchAboutBlank,
2881
- exclude_globs: options.excludeGlobs,
2882
- exclude_matches: options.excludeMatches,
2883
- include_globs: options.includeGlobs,
2884
- run_at: options.runAt,
2885
- css,
2886
- js,
2887
- // @ts-expect-error: untyped chrome options
2888
- match_origin_as_fallback: options.matchOriginAsFallback,
2889
- world: options.world
2890
- };
2891
- }
2892
- function mapWxtOptionsToRegisteredContentScript(options, js, css) {
2893
- return {
2894
- allFrames: options.allFrames,
2895
- excludeMatches: options.excludeMatches,
2896
- matches: options.matches,
2897
- runAt: options.runAt,
2898
- js,
2899
- css,
2900
- // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
2901
- world: options.world
2902
- };
2903
- }
2904
- function getContentScriptJs(config, entrypoint) {
2905
- return [getEntrypointBundlePath(entrypoint, config.outDir, ".js")];
2906
- }
2907
-
2908
- // src/core/utils/package.ts
2909
- import { resolve as resolve13 } from "node:path";
2910
- import fs11 from "fs-extra";
2911
- async function getPackageJson() {
2912
- const file = resolve13(wxt.config.root, "package.json");
2913
- try {
2914
- return await fs11.readJson(file);
2915
- } catch (err) {
2916
- wxt.logger.debug(
2917
- `Failed to read package.json at: ${file}. Returning undefined.`
2918
- );
2919
- return {};
2920
- }
2921
- }
2922
-
2923
- // src/core/utils/manifest.ts
2924
- import defu2 from "defu";
2925
- async function writeManifest(manifest, output) {
2926
- const str = wxt.config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
2927
- await fs12.ensureDir(wxt.config.outDir);
2928
- await writeFileIfDifferent(resolve14(wxt.config.outDir, "manifest.json"), str);
2929
- output.publicAssets.unshift({
2930
- type: "asset",
2931
- fileName: "manifest.json"
2932
- });
2933
- }
2934
- async function generateManifest(entrypoints, buildOutput) {
2935
- const warnings = [];
2936
- const pkg = await getPackageJson();
2937
- let versionName = wxt.config.manifest.version_name ?? wxt.config.manifest.version ?? pkg?.version;
2938
- if (versionName == null) {
2939
- versionName = "0.0.0";
2940
- wxt.logger.warn(
2941
- 'Extension version not found, defaulting to "0.0.0". Add a version to your `package.json` or `wxt.config.ts` file. For more details, see: https://wxt.dev/guide/manifest.html#version-and-version-name'
2942
- );
2943
- }
2944
- const version2 = wxt.config.manifest.version ?? simplifyVersion(versionName);
2945
- const baseManifest = {
2946
- manifest_version: wxt.config.manifestVersion,
2947
- name: pkg?.name,
2948
- description: pkg?.description,
2949
- version: version2,
2950
- short_name: pkg?.shortName,
2951
- icons: discoverIcons(buildOutput)
2952
- };
2953
- const userManifest = wxt.config.manifest;
2954
- let manifest = defu2(
2955
- userManifest,
2956
- baseManifest
2957
- );
2958
- if (wxt.config.command === "serve" && wxt.config.dev.reloadCommand) {
2959
- if (manifest.commands && Object.keys(manifest.commands).length >= 4) {
2960
- warnings.push([
2961
- "Extension already has 4 registered commands, WXT's reload command is disabled"
2962
- ]);
2963
- } else {
2964
- manifest.commands ??= {};
2965
- manifest.commands["wxt:reload-extension"] = {
2966
- description: "Reload the extension during development",
2967
- suggested_key: {
2968
- default: wxt.config.dev.reloadCommand
2969
- }
2970
- };
2971
- }
2972
- }
2973
- manifest.version = version2;
2974
- manifest.version_name = // Firefox doesn't support version_name
2975
- wxt.config.browser === "firefox" || versionName === version2 ? void 0 : versionName;
2976
- addEntrypoints(manifest, entrypoints, buildOutput);
2977
- if (wxt.config.command === "serve") addDevModeCsp(manifest);
2978
- if (wxt.config.command === "serve") addDevModePermissions(manifest);
2979
- wxt.config.transformManifest?.(manifest);
2980
- await wxt.hooks.callHook("build:manifestGenerated", wxt, manifest);
2981
- if (wxt.config.manifestVersion === 2) {
2982
- convertWebAccessibleResourcesToMv2(manifest);
2983
- convertActionToMv2(manifest);
2984
- moveHostPermissionsToPermissions(manifest);
2985
- }
2986
- if (wxt.config.manifestVersion === 3) {
2987
- validateMv3WebAccessbileResources(manifest);
2988
- }
2989
- stripKeys(manifest);
2990
- if (manifest.name == null)
2991
- throw Error(
2992
- "Manifest 'name' is missing. Either:\n1. Set the name in your <rootDir>/package.json\n2. Set a name via the manifest option in your wxt.config.ts"
2993
- );
2994
- if (manifest.version == null) {
2995
- throw Error(
2996
- "Manifest 'version' is missing. Either:\n1. Add a version in your <rootDir>/package.json\n2. Pass the version via the manifest option in your wxt.config.ts"
2997
- );
2998
- }
2999
- return {
3000
- manifest,
3001
- warnings
3002
- };
3003
- }
3004
- function simplifyVersion(versionName) {
3005
- const version2 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
3006
- versionName
3007
- )?.[1];
3008
- if (version2 == null)
3009
- throw Error(
3010
- `Cannot simplify package.json version "${versionName}" to a valid extension version, "X.Y.Z"`
3011
- );
3012
- return version2;
3013
- }
3014
- function addEntrypoints(manifest, entrypoints, buildOutput) {
3015
- const entriesByType = entrypoints.reduce((map, entrypoint) => {
3016
- map[entrypoint.type] ??= [];
3017
- map[entrypoint.type]?.push(entrypoint);
3018
- return map;
3019
- }, {});
3020
- const background = entriesByType["background"]?.[0];
3021
- const bookmarks = entriesByType["bookmarks"]?.[0];
3022
- const contentScripts = entriesByType["content-script"];
3023
- const devtools = entriesByType["devtools"]?.[0];
3024
- const history = entriesByType["history"]?.[0];
3025
- const newtab = entriesByType["newtab"]?.[0];
3026
- const options = entriesByType["options"]?.[0];
3027
- const popup = entriesByType["popup"]?.[0];
3028
- const sandboxes = entriesByType["sandbox"];
3029
- const sidepanels = entriesByType["sidepanel"];
3030
- if (background) {
3031
- const script = getEntrypointBundlePath(
3032
- background,
3033
- wxt.config.outDir,
3034
- ".js"
3035
- );
3036
- if (wxt.config.browser === "firefox" && wxt.config.manifestVersion === 3) {
3037
- manifest.background = {
3038
- type: background.options.type,
3039
- scripts: [script]
3040
- };
3041
- } else if (wxt.config.manifestVersion === 3) {
3042
- manifest.background = {
3043
- type: background.options.type,
3044
- service_worker: script
3045
- };
3046
- } else {
3047
- manifest.background = {
3048
- persistent: background.options.persistent,
3049
- scripts: [script]
3050
- };
3051
- }
3052
- }
3053
- if (bookmarks) {
3054
- if (wxt.config.browser === "firefox") {
3055
- wxt.logger.warn(
3056
- "Bookmarks are not supported by Firefox. chrome_url_overrides.bookmarks was not added to the manifest"
3057
- );
3058
- } else {
3059
- manifest.chrome_url_overrides ??= {};
3060
- manifest.chrome_url_overrides.bookmarks = getEntrypointBundlePath(
3061
- bookmarks,
3062
- wxt.config.outDir,
3063
- ".html"
3064
- );
3065
- }
3066
- }
3067
- if (history) {
3068
- if (wxt.config.browser === "firefox") {
3069
- wxt.logger.warn(
3070
- "Bookmarks are not supported by Firefox. chrome_url_overrides.history was not added to the manifest"
3071
- );
3072
- } else {
3073
- manifest.chrome_url_overrides ??= {};
3074
- manifest.chrome_url_overrides.history = getEntrypointBundlePath(
3075
- history,
3076
- wxt.config.outDir,
3077
- ".html"
3078
- );
3079
- }
3080
- }
3081
- if (newtab) {
3082
- manifest.chrome_url_overrides ??= {};
3083
- manifest.chrome_url_overrides.newtab = getEntrypointBundlePath(
3084
- newtab,
3085
- wxt.config.outDir,
3086
- ".html"
3087
- );
3088
- }
3089
- if (popup) {
3090
- const default_popup = getEntrypointBundlePath(
3091
- popup,
3092
- wxt.config.outDir,
3093
- ".html"
3094
- );
3095
- const options2 = {};
3096
- if (popup.options.defaultIcon)
3097
- options2.default_icon = popup.options.defaultIcon;
3098
- if (popup.options.defaultTitle)
3099
- options2.default_title = popup.options.defaultTitle;
3100
- if (popup.options.browserStyle)
3101
- options2.browser_style = popup.options.browserStyle;
3102
- if (manifest.manifest_version === 3) {
3103
- manifest.action = {
3104
- ...manifest.action ?? {},
3105
- ...options2,
3106
- default_popup
3107
- };
3108
- } else {
3109
- const key = popup.options.mv2Key ?? "browser_action";
3110
- manifest[key] = {
3111
- ...manifest[key] ?? {},
3112
- ...options2,
3113
- default_popup
3114
- };
3115
- }
3116
- }
3117
- if (devtools) {
3118
- manifest.devtools_page = getEntrypointBundlePath(
3119
- devtools,
3120
- wxt.config.outDir,
3121
- ".html"
3122
- );
3123
- }
3124
- if (options) {
3125
- const page = getEntrypointBundlePath(options, wxt.config.outDir, ".html");
3126
- manifest.options_ui = {
3127
- open_in_tab: options.options.openInTab,
3128
- browser_style: wxt.config.browser === "firefox" ? options.options.browserStyle : void 0,
3129
- chrome_style: wxt.config.browser !== "firefox" ? options.options.chromeStyle : void 0,
3130
- page
3131
- };
3132
- }
3133
- if (sandboxes?.length) {
3134
- if (wxt.config.browser === "firefox") {
3135
- wxt.logger.warn(
3136
- "Sandboxed pages not supported by Firefox. sandbox.pages was not added to the manifest"
3137
- );
3138
- } else {
3139
- manifest.sandbox = {
3140
- pages: sandboxes.map(
3141
- (entry) => getEntrypointBundlePath(entry, wxt.config.outDir, ".html")
3142
- )
3143
- };
3144
- }
3145
- }
3146
- if (sidepanels?.length) {
3147
- const defaultSidepanel = sidepanels.find((entry) => entry.name === "sidepanel") ?? sidepanels[0];
3148
- const page = getEntrypointBundlePath(
3149
- defaultSidepanel,
3150
- wxt.config.outDir,
3151
- ".html"
3152
- );
3153
- if (wxt.config.browser === "firefox") {
3154
- manifest.sidebar_action = {
3155
- default_panel: page,
3156
- browser_style: defaultSidepanel.options.browserStyle,
3157
- default_icon: defaultSidepanel.options.defaultIcon,
3158
- default_title: defaultSidepanel.options.defaultTitle,
3159
- open_at_install: defaultSidepanel.options.openAtInstall
3160
- };
3161
- } else if (wxt.config.manifestVersion === 3) {
3162
- manifest.side_panel = {
3163
- default_path: page
3164
- };
3165
- addPermission(manifest, "sidePanel");
3166
- } else {
3167
- wxt.logger.warn(
3168
- "Side panel not supported by Chromium using MV2. side_panel.default_path was not added to the manifest"
3169
- );
3170
- }
3171
- }
3172
- if (contentScripts?.length) {
3173
- const cssMap = getContentScriptsCssMap(buildOutput, contentScripts);
3174
- if (wxt.config.command === "serve" && wxt.config.manifestVersion === 3) {
3175
- contentScripts.forEach((script) => {
3176
- script.options.matches.forEach((matchPattern) => {
3177
- addHostPermission(manifest, matchPattern);
3178
- });
3179
- });
3180
- } else {
3181
- const hashToEntrypointsMap = contentScripts.filter((cs) => cs.options.registration !== "runtime").reduce((map, script) => {
3182
- const hash = hashContentScriptOptions(script.options);
3183
- if (map.has(hash)) map.get(hash)?.push(script);
3184
- else map.set(hash, [script]);
3185
- return map;
3186
- }, /* @__PURE__ */ new Map());
3187
- const manifestContentScripts = Array.from(
3188
- hashToEntrypointsMap.values()
3189
- ).map(
3190
- (scripts) => mapWxtOptionsToContentScript(
3191
- scripts[0].options,
3192
- scripts.map(
3193
- (entry) => getEntrypointBundlePath(entry, wxt.config.outDir, ".js")
3194
- ),
3195
- getContentScriptCssFiles(scripts, cssMap)
3196
- )
3197
- );
3198
- if (manifestContentScripts.length >= 0) {
3199
- manifest.content_scripts ??= [];
3200
- manifest.content_scripts.push(...manifestContentScripts);
3201
- }
3202
- const runtimeContentScripts = contentScripts.filter(
3203
- (cs) => cs.options.registration === "runtime"
3204
- );
3205
- if (runtimeContentScripts.length > 0 && wxt.config.manifestVersion === 2) {
3206
- throw Error(
3207
- 'Cannot use `registration: "runtime"` with MV2 content scripts, it is a MV3-only feature.'
3208
- );
3209
- }
3210
- runtimeContentScripts.forEach((script) => {
3211
- script.options.matches.forEach((matchPattern) => {
3212
- addHostPermission(manifest, matchPattern);
3213
- });
3214
- });
3215
- }
3216
- const contentScriptCssResources = getContentScriptCssWebAccessibleResources(
3217
- contentScripts,
3218
- cssMap
3219
- );
3220
- if (contentScriptCssResources.length > 0) {
3221
- manifest.web_accessible_resources ??= [];
3222
- manifest.web_accessible_resources.push(...contentScriptCssResources);
3223
- }
3224
- }
3225
- }
3226
- function discoverIcons(buildOutput) {
3227
- const icons = [];
3228
- const iconRegex = [
3229
- /^icon-([0-9]+)\.png$/,
3230
- // icon-16.png
3231
- /^icon-([0-9]+)x[0-9]+\.png$/,
3232
- // icon-16x16.png
3233
- /^icon@([0-9]+)w\.png$/,
3234
- // icon@16w.png
3235
- /^icon@([0-9]+)h\.png$/,
3236
- // icon@16h.png
3237
- /^icon@([0-9]+)\.png$/,
3238
- // icon@16.png
3239
- /^icons?[\/\\]([0-9]+)\.png$/,
3240
- // icon/16.png | icons/16.png
3241
- /^icons?[\/\\]([0-9]+)x[0-9]+\.png$/
3242
- // icon/16x16.png | icons/16x16.png
3243
- ];
3244
- buildOutput.publicAssets.forEach((asset) => {
3245
- let size;
3246
- for (const regex of iconRegex) {
3247
- const match = asset.fileName.match(regex);
3248
- if (match?.[1] != null) {
3249
- size = match[1];
3250
- break;
3251
- }
3252
- }
3253
- if (size == null) return;
3254
- icons.push([size, normalizePath(asset.fileName)]);
3255
- });
3256
- return icons.length > 0 ? Object.fromEntries(icons) : void 0;
3257
- }
3258
- function addDevModeCsp(manifest) {
3259
- const permission = `http://${wxt.server?.hostname ?? ""}/*`;
3260
- const allowedCsp = wxt.server?.origin ?? "http://localhost:*";
3261
- if (manifest.manifest_version === 3) {
3262
- addHostPermission(manifest, permission);
3263
- } else {
3264
- addPermission(manifest, permission);
3265
- }
3266
- const extensionPagesCsp = new ContentSecurityPolicy(
3267
- manifest.manifest_version === 3 ? (
3268
- // @ts-expect-error: extension_pages is not typed
3269
- manifest.content_security_policy?.extension_pages ?? "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
3270
- ) : manifest.content_security_policy ?? "script-src 'self'; object-src 'self';"
3271
- // default CSP for MV2
3272
- );
3273
- const sandboxCsp = new ContentSecurityPolicy(
3274
- // @ts-expect-error: sandbox is not typed
3275
- manifest.content_security_policy?.sandbox ?? "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';"
3276
- // default sandbox CSP for MV3
3277
- );
3278
- if (wxt.server) {
3279
- extensionPagesCsp.add("script-src", allowedCsp);
3280
- sandboxCsp.add("script-src", allowedCsp);
3281
- }
3282
- if (manifest.manifest_version === 3) {
3283
- manifest.content_security_policy ??= {};
3284
- manifest.content_security_policy.extension_pages = extensionPagesCsp.toString();
3285
- manifest.content_security_policy.sandbox = sandboxCsp.toString();
3286
- } else {
3287
- manifest.content_security_policy = extensionPagesCsp.toString();
3288
- }
3289
- }
3290
- function addDevModePermissions(manifest) {
3291
- addPermission(manifest, "tabs");
3292
- if (wxt.config.manifestVersion === 3) addPermission(manifest, "scripting");
3293
- }
3294
- function getContentScriptCssFiles(contentScripts, contentScriptCssMap) {
3295
- const css = [];
3296
- contentScripts.forEach((script) => {
3297
- if (script.options.cssInjectionMode === "manual" || script.options.cssInjectionMode === "ui")
3298
- return;
3299
- const cssFile = contentScriptCssMap[script.name];
3300
- if (cssFile == null) return;
3301
- if (cssFile) css.push(cssFile);
3302
- });
3303
- if (css.length > 0) return css;
3304
- return void 0;
3305
- }
3306
- function getContentScriptCssWebAccessibleResources(contentScripts, contentScriptCssMap) {
3307
- const resources = [];
3308
- contentScripts.forEach((script) => {
3309
- if (script.options.cssInjectionMode !== "ui") return;
3310
- const cssFile = contentScriptCssMap[script.name];
3311
- if (cssFile == null) return;
3312
- resources.push({
3313
- resources: [cssFile],
3314
- matches: script.options.matches.map(
3315
- (matchPattern) => stripPathFromMatchPattern(matchPattern)
3316
- )
3317
- });
3318
- });
3319
- return resources;
3320
- }
3321
- function getContentScriptsCssMap(buildOutput, scripts) {
3322
- const map = {};
3323
- const allChunks = buildOutput.steps.flatMap((step) => step.chunks);
3324
- scripts.forEach((script) => {
3325
- const relatedCss = allChunks.find(
3326
- (chunk) => chunk.fileName === `content-scripts/${script.name}.css`
3327
- );
3328
- if (relatedCss != null) map[script.name] = relatedCss.fileName;
3329
- });
3330
- return map;
3331
- }
3332
- function addPermission(manifest, permission) {
3333
- manifest.permissions ??= [];
3334
- if (manifest.permissions.includes(permission)) return;
3335
- manifest.permissions.push(permission);
3336
- }
3337
- function addHostPermission(manifest, hostPermission) {
3338
- manifest.host_permissions ??= [];
3339
- if (manifest.host_permissions.includes(hostPermission)) return;
3340
- manifest.host_permissions.push(hostPermission);
3341
- }
3342
- function stripPathFromMatchPattern(pattern) {
3343
- const protocolSepIndex = pattern.indexOf("://");
3344
- if (protocolSepIndex === -1) return pattern;
3345
- const startOfPath = pattern.indexOf("/", protocolSepIndex + 3);
3346
- return pattern.substring(0, startOfPath) + "/*";
3347
- }
3348
- function convertWebAccessibleResourcesToMv2(manifest) {
3349
- if (manifest.web_accessible_resources == null) return;
3350
- manifest.web_accessible_resources = Array.from(
3351
- new Set(
3352
- manifest.web_accessible_resources.flatMap((item) => {
3353
- if (typeof item === "string") return item;
3354
- return item.resources;
3355
- })
3356
- )
3357
- );
3358
- }
3359
- function moveHostPermissionsToPermissions(manifest) {
3360
- if (!manifest.host_permissions?.length) return;
3361
- manifest.host_permissions.forEach(
3362
- (permission) => addPermission(manifest, permission)
3363
- );
3364
- delete manifest.host_permissions;
3365
- }
3366
- function convertActionToMv2(manifest) {
3367
- if (manifest.action == null || manifest.browser_action != null || manifest.page_action != null)
3368
- return;
3369
- manifest.browser_action = manifest.action;
3370
- }
3371
- function validateMv3WebAccessbileResources(manifest) {
3372
- if (manifest.web_accessible_resources == null) return;
3373
- const stringResources = manifest.web_accessible_resources.filter(
3374
- (item) => typeof item === "string"
3375
- );
3376
- if (stringResources.length > 0) {
3377
- throw Error(
3378
- `Non-MV3 web_accessible_resources detected: ${JSON.stringify(
3379
- stringResources
3380
- )}. When manually defining web_accessible_resources, define them as MV3 objects ({ matches: [...], resources: [...] }), and WXT will automatically convert them to MV2 when necessary.`
3381
- );
3382
- }
3383
- }
3384
- function stripKeys(manifest) {
3385
- let keysToRemove = [];
3386
- if (wxt.config.manifestVersion === 2) {
3387
- keysToRemove.push(...mv3OnlyKeys);
3388
- if (wxt.config.browser === "firefox")
3389
- keysToRemove.push(...firefoxMv3OnlyKeys);
3390
- } else {
3391
- keysToRemove.push(...mv2OnlyKeys);
3392
- }
3393
- keysToRemove.forEach((key) => {
3394
- delete manifest[key];
3395
- });
3396
- }
3397
- var mv2OnlyKeys = [
3398
- "page_action",
3399
- "browser_action",
3400
- "automation",
3401
- "content_capabilities",
3402
- "converted_from_user_script",
3403
- "current_locale",
3404
- "differential_fingerprint",
3405
- "event_rules",
3406
- "file_browser_handlers",
3407
- "file_system_provider_capabilities",
3408
- "input_components",
3409
- "nacl_modules",
3410
- "natively_connectable",
3411
- "offline_enabled",
3412
- "platforms",
3413
- "replacement_web_app",
3414
- "system_indicator",
3415
- "user_scripts"
3416
- ];
3417
- var mv3OnlyKeys = [
3418
- "action",
3419
- "export",
3420
- "optional_host_permissions",
3421
- "side_panel"
3422
- ];
3423
- var firefoxMv3OnlyKeys = ["host_permissions"];
3424
-
3425
- // src/core/utils/building/rebuild.ts
3426
- async function rebuild(allEntrypoints, entrypointGroups, existingOutput = {
3427
- steps: [],
3428
- publicAssets: []
3429
- }) {
3430
- const { default: ora } = await import("ora");
3431
- const spinner = ora(`Preparing...`).start();
3432
- await generateTypesDir(allEntrypoints).catch((err) => {
3433
- wxt.logger.warn("Failed to update .wxt directory:", err);
3434
- if (wxt.config.command === "build") throw err;
3435
- });
3436
- const newOutput = await buildEntrypoints(entrypointGroups, spinner);
3437
- const mergedOutput = {
3438
- steps: [...existingOutput.steps, ...newOutput.steps],
3439
- publicAssets: [...existingOutput.publicAssets, ...newOutput.publicAssets]
3440
- };
3441
- const { manifest: newManifest, warnings: manifestWarnings } = await generateManifest(allEntrypoints, mergedOutput);
3442
- const finalOutput = {
3443
- manifest: newManifest,
3444
- ...newOutput
3445
- };
3446
- await writeManifest(newManifest, finalOutput);
3447
- spinner.clear().stop();
3448
- return {
3449
- output: {
3450
- manifest: newManifest,
3451
- steps: [...existingOutput.steps, ...finalOutput.steps],
3452
- publicAssets: [
3453
- ...existingOutput.publicAssets,
3454
- ...finalOutput.publicAssets
3455
- ]
3456
- },
3457
- manifest: newManifest,
3458
- warnings: manifestWarnings
3459
- };
3460
- }
3461
-
3462
- // src/core/utils/building/internal-build.ts
3463
- import { relative as relative6 } from "node:path";
3464
-
3465
- // src/core/utils/validation.ts
3466
- function validateEntrypoints(entrypoints) {
3467
- const errors = entrypoints.flatMap((entrypoint) => {
3468
- switch (entrypoint.type) {
3469
- case "content-script":
3470
- return validateContentScriptEntrypoint(entrypoint);
3471
- default:
3472
- return validateBaseEntrypoint(entrypoint);
3473
- }
3474
- });
3475
- let errorCount = 0;
3476
- let warningCount = 0;
3477
- for (const err of errors) {
3478
- if (err.type === "warning") warningCount++;
3479
- else errorCount++;
3480
- }
3481
- return {
3482
- errors,
3483
- errorCount,
3484
- warningCount
3485
- };
3486
- }
3487
- function validateContentScriptEntrypoint(definition) {
3488
- const errors = validateBaseEntrypoint(definition);
3489
- if (definition.options.matches == null) {
3490
- errors.push({
3491
- type: "error",
3492
- message: "`matches` is required",
3493
- value: definition.options.matches,
3494
- entrypoint: definition
3495
- });
3496
- }
3497
- return errors;
3498
- }
3499
- function validateBaseEntrypoint(definition) {
3500
- const errors = [];
3501
- if (definition.options.exclude != null && !Array.isArray(definition.options.exclude)) {
3502
- errors.push({
3503
- type: "error",
3504
- message: "`exclude` must be an array of browser names",
3505
- value: definition.options.exclude,
3506
- entrypoint: definition
3507
- });
3508
- }
3509
- if (definition.options.include != null && !Array.isArray(definition.options.include)) {
3510
- errors.push({
3511
- type: "error",
3512
- message: "`include` must be an array of browser names",
3513
- value: definition.options.include,
3514
- entrypoint: definition
3515
- });
3516
- }
3517
- return errors;
3518
- }
3519
- var ValidationError = class extends Error {
3520
- };
3521
-
3522
- // src/core/utils/building/internal-build.ts
3523
- import { mergeJsonOutputs } from "@aklinker1/rollup-plugin-visualizer";
3524
- import { isCI } from "ci-info";
3525
- async function internalBuild() {
3526
- await wxt.hooks.callHook("build:before", wxt);
3527
- const verb = wxt.config.command === "serve" ? "Pre-rendering" : "Building";
3528
- const target = `${wxt.config.browser}-mv${wxt.config.manifestVersion}`;
3529
- wxt.logger.info(
3530
- `${verb} ${pc5.cyan(target)} for ${pc5.cyan(wxt.config.mode)} with ${pc5.green(
3531
- `${wxt.builder.name} ${wxt.builder.version}`
3532
- )}`
3533
- );
3534
- const startTime = Date.now();
3535
- await fs13.rm(wxt.config.outDir, { recursive: true, force: true });
3536
- await fs13.ensureDir(wxt.config.outDir);
3537
- const entrypoints = await findEntrypoints();
3538
- wxt.logger.debug("Detected entrypoints:", entrypoints);
3539
- const validationResults = validateEntrypoints(entrypoints);
3540
- if (validationResults.errorCount + validationResults.warningCount > 0) {
3541
- printValidationResults(validationResults);
3542
- }
3543
- if (validationResults.errorCount > 0) {
3544
- throw new ValidationError(`Entrypoint validation failed`, {
3545
- cause: validationResults
3546
- });
3547
- }
3548
- const groups = groupEntrypoints(entrypoints);
3549
- await wxt.hooks.callHook("entrypoints:grouped", wxt, groups);
3550
- const { output, warnings } = await rebuild(entrypoints, groups, void 0);
3551
- await wxt.hooks.callHook("build:done", wxt, output);
3552
- await printBuildSummary(
3553
- wxt.logger.success,
3554
- `Built extension in ${formatDuration(Date.now() - startTime)}`,
3555
- output
3556
- );
3557
- for (const warning of warnings) {
3558
- wxt.logger.warn(...warning);
3559
- }
3560
- if (wxt.config.analysis.enabled) {
3561
- await combineAnalysisStats();
3562
- const statsPath = relative6(wxt.config.root, wxt.config.analysis.outputFile);
3563
- wxt.logger.info(
3564
- `Analysis complete:
3565
- ${pc5.gray("\u2514\u2500")} ${pc5.yellow(statsPath)}`
3566
- );
3567
- if (wxt.config.analysis.open) {
3568
- if (isCI) {
3569
- wxt.logger.debug(`Skipped opening ${pc5.yellow(statsPath)} in CI`);
3570
- } else {
3571
- wxt.logger.info(`Opening ${pc5.yellow(statsPath)} in browser...`);
3572
- const { default: open } = await import("open");
3573
- open(wxt.config.analysis.outputFile);
3574
- }
3575
- }
3576
- }
3577
- return output;
3578
- }
3579
- async function combineAnalysisStats() {
3580
- const unixFiles = await glob5(`${wxt.config.analysis.outputName}-*.json`, {
3581
- cwd: wxt.config.analysis.outputDir,
3582
- absolute: true
3583
- });
3584
- const absolutePaths = unixFiles.map(unnormalizePath);
3585
- await mergeJsonOutputs({
3586
- inputs: absolutePaths,
3587
- template: wxt.config.analysis.template,
3588
- filename: wxt.config.analysis.outputFile
3589
- });
3590
- if (!wxt.config.analysis.keepArtifacts) {
3591
- await Promise.all(absolutePaths.map((statsFile) => fs13.remove(statsFile)));
3592
- }
3593
- }
3594
- function printValidationResults({
3595
- errorCount,
3596
- errors,
3597
- warningCount
3598
- }) {
3599
- (errorCount > 0 ? wxt.logger.error : wxt.logger.warn)(
3600
- `Entrypoint validation failed: ${errorCount} error${errorCount === 1 ? "" : "s"}, ${warningCount} warning${warningCount === 1 ? "" : "s"}`
3601
- );
3602
- const cwd = process.cwd();
3603
- const entrypointErrors = errors.reduce((map, error) => {
3604
- const entryErrors = map.get(error.entrypoint) ?? [];
3605
- entryErrors.push(error);
3606
- map.set(error.entrypoint, entryErrors);
3607
- return map;
3608
- }, /* @__PURE__ */ new Map());
3609
- Array.from(entrypointErrors.entries()).forEach(([entrypoint, errors2]) => {
3610
- wxt.logger.log(relative6(cwd, entrypoint.inputPath));
3611
- console.log();
3612
- errors2.forEach((err) => {
3613
- const type = err.type === "error" ? pc5.red("ERROR") : pc5.yellow("WARN");
3614
- const recieved = pc5.dim(`(recieved: ${JSON.stringify(err.value)})`);
3615
- wxt.logger.log(` - ${type} ${err.message} ${recieved}`);
3616
- });
3617
- console.log();
3618
- });
3619
- }
3620
-
3621
- // src/core/build.ts
3622
- async function build(config) {
3623
- await registerWxt("build", config);
3624
- return await internalBuild();
3625
- }
3626
-
3627
- // src/core/clean.ts
3628
- import path8 from "node:path";
3629
- import glob6 from "fast-glob";
3630
- import fs14 from "fs-extra";
3631
- import pc6 from "picocolors";
3632
- async function clean(config) {
3633
- if (typeof config === "string") {
3634
- config = { root: config };
3635
- }
3636
- await registerWxt("build", config);
3637
- wxt.logger.info("Cleaning Project");
3638
- const root = wxt.config.root;
3639
- const tempDirs = [
3640
- "node_modules/.vite",
3641
- "node_modules/.cache",
3642
- "**/.wxt",
3643
- `${path8.relative(root, wxt.config.outBaseDir)}/*`
3644
- ];
3645
- wxt.logger.debug("Looking for:", tempDirs.map(pc6.cyan).join(", "));
3646
- const directories = await glob6(tempDirs, {
3647
- cwd: root,
3648
- absolute: true,
3649
- onlyDirectories: true,
3650
- deep: 2
3651
- });
3652
- if (directories.length === 0) {
3653
- wxt.logger.debug("No generated files found.");
3654
- return;
3655
- }
3656
- wxt.logger.debug(
3657
- "Found:",
3658
- directories.map((dir) => pc6.cyan(path8.relative(root, dir))).join(", ")
3659
- );
3660
- for (const directory of directories) {
3661
- await fs14.rm(directory, { force: true, recursive: true });
3662
- wxt.logger.debug("Deleted " + pc6.cyan(path8.relative(root, directory)));
3663
- }
3664
- }
3665
-
3666
- // src/core/runners/wsl.ts
3667
- import { relative as relative7 } from "node:path";
3668
- function createWslRunner() {
3669
- return {
3670
- async openBrowser() {
3671
- wxt.logger.warn(
3672
- `Cannot open browser when using WSL. Load "${relative7(
3673
- process.cwd(),
3674
- wxt.config.outDir
3675
- )}" as an unpacked extension manually`
3676
- );
3677
- },
3678
- async closeBrowser() {
3679
- }
3680
- };
3681
- }
3682
-
3683
- // src/core/runners/web-ext.ts
3684
- import defu3 from "defu";
3685
- function createWebExtRunner() {
3686
- let runner;
3687
- return {
3688
- async openBrowser() {
3689
- const startTime = Date.now();
3690
- if (wxt.config.browser === "firefox" && wxt.config.manifestVersion === 3) {
3691
- throw Error(
3692
- "Dev mode does not support Firefox MV3. For alternatives, see https://github.com/wxt-dev/wxt/issues/230#issuecomment-1806881653"
3693
- );
3694
- }
3695
- const webExtLogger = await import("web-ext-run/util/logger");
3696
- webExtLogger.consoleStream.write = ({ level, msg, name }) => {
3697
- if (level >= ERROR_LOG_LEVEL) wxt.logger.error(name, msg);
3698
- if (level >= WARN_LOG_LEVEL) wxt.logger.warn(msg);
3699
- };
3700
- const wxtUserConfig = wxt.config.runnerConfig.config;
3701
- const userConfig = {
3702
- console: wxtUserConfig?.openConsole,
3703
- devtools: wxtUserConfig?.openDevtools,
3704
- startUrl: wxtUserConfig?.startUrls,
3705
- keepProfileChanges: wxtUserConfig?.keepProfileChanges,
3706
- ...wxt.config.browser === "firefox" ? {
3707
- firefox: wxtUserConfig?.binaries?.firefox,
3708
- firefoxProfile: wxtUserConfig?.firefoxProfile,
3709
- prefs: wxtUserConfig?.firefoxPrefs,
3710
- args: wxtUserConfig?.firefoxArgs
3711
- } : {
3712
- chromiumBinary: wxtUserConfig?.binaries?.[wxt.config.browser],
3713
- chromiumProfile: wxtUserConfig?.chromiumProfile,
3714
- chromiumPref: defu3(
3715
- wxtUserConfig?.chromiumPref,
3716
- DEFAULT_CHROMIUM_PREFS
3717
- ),
3718
- args: wxtUserConfig?.chromiumArgs
3719
- }
3720
- };
3721
- const finalConfig = {
3722
- ...userConfig,
3723
- target: wxt.config.browser === "firefox" ? "firefox-desktop" : "chromium",
3724
- sourceDir: wxt.config.outDir,
3725
- // Don't add a "Reload Manager" extension alongside dev extension, WXT
3726
- // already handles reloads intenrally.
3727
- noReloadManagerExtension: true,
3728
- // WXT handles reloads, so disable auto-reload behaviors in web-ext
3729
- noReload: true,
3730
- noInput: true
3731
- };
3732
- const options = {
3733
- // Don't call `process.exit(0)` after starting web-ext
3734
- shouldExitProgram: false
3735
- };
3736
- wxt.logger.debug("web-ext config:", finalConfig);
3737
- wxt.logger.debug("web-ext options:", options);
3738
- const webExt = await import("web-ext-run");
3739
- runner = await webExt.default.cmd.run(finalConfig, options);
3740
- const duration = Date.now() - startTime;
3741
- wxt.logger.success(`Opened browser in ${formatDuration(duration)}`);
3742
- },
3743
- async closeBrowser() {
3744
- return await runner?.exit();
3745
- }
3746
- };
3747
- }
3748
- var WARN_LOG_LEVEL = 40;
3749
- var ERROR_LOG_LEVEL = 50;
3750
- var DEFAULT_CHROMIUM_PREFS = {
3751
- devtools: {
3752
- synced_preferences_sync_disabled: {
3753
- // Remove content scripts from sourcemap debugger ignore list so stack traces
3754
- // and log locations show up properly, see:
3755
- // https://github.com/wxt-dev/wxt/issues/236#issuecomment-1915364520
3756
- skipContentScripts: false
3757
- }
3758
- }
3759
- };
3760
-
3761
- // src/core/runners/safari.ts
3762
- import { relative as relative8 } from "node:path";
3763
- function createSafariRunner() {
3764
- return {
3765
- async openBrowser() {
3766
- wxt.logger.warn(
3767
- `Cannot Safari using web-ext. Load "${relative8(
3768
- process.cwd(),
3769
- wxt.config.outDir
3770
- )}" as an unpacked extension manually`
3771
- );
3772
- },
3773
- async closeBrowser() {
3774
- }
3775
- };
3776
- }
3777
-
3778
- // src/core/runners/manual.ts
3779
- import { relative as relative9 } from "node:path";
3780
- function createManualRunner() {
3781
- return {
3782
- async openBrowser() {
3783
- wxt.logger.info(
3784
- `Load "${relative9(
3785
- process.cwd(),
3786
- wxt.config.outDir
3787
- )}" as an unpacked extension manually`
3788
- );
3789
- },
3790
- async closeBrowser() {
3791
- }
3792
- };
3793
- }
3794
-
3795
- // src/core/utils/wsl.ts
3796
- async function isWsl() {
3797
- const { default: isWsl2 } = await import("is-wsl");
3798
- return isWsl2;
3799
- }
3800
-
3801
- // src/core/runners/index.ts
3802
- async function createExtensionRunner() {
3803
- if (wxt.config.browser === "safari") return createSafariRunner();
3804
- if (await isWsl()) return createWslRunner();
3805
- if (wxt.config.runnerConfig.config?.disabled) return createManualRunner();
3806
- return createWebExtRunner();
3807
- }
3808
-
3809
- // src/core/create-server.ts
3810
- import { Mutex } from "async-mutex";
3811
- import pc7 from "picocolors";
3812
- import { relative as relative10 } from "node:path";
3813
- async function createServer(inlineConfig) {
3814
- await registerWxt("serve", inlineConfig, async (config) => {
3815
- const { port, hostname } = config.dev.server;
3816
- const serverInfo = {
3817
- port,
3818
- hostname,
3819
- origin: `http://${hostname}:${port}`
3820
- };
3821
- const server2 = {
3822
- ...serverInfo,
3823
- get watcher() {
3824
- return builderServer.watcher;
3825
- },
3826
- get ws() {
3827
- return builderServer.ws;
3828
- },
3829
- currentOutput: void 0,
3830
- async start() {
3831
- await builderServer.listen();
3832
- wxt.logger.success(`Started dev server @ ${serverInfo.origin}`);
3833
- await buildAndOpenBrowser();
3834
- },
3835
- async stop() {
3836
- await runner.closeBrowser();
3837
- await builderServer.close();
3838
- },
3839
- async restart() {
3840
- await closeAndRecreateRunner();
3841
- await buildAndOpenBrowser();
3842
- },
3843
- transformHtml(url, html, originalUrl) {
3844
- return builderServer.transformHtml(url, html, originalUrl);
3845
- },
3846
- reloadContentScript(payload) {
3847
- server2.ws.send("wxt:reload-content-script", payload);
3848
- },
3849
- reloadPage(path11) {
3850
- server2.ws.send("wxt:reload-page", path11);
3851
- },
3852
- reloadExtension() {
3853
- server2.ws.send("wxt:reload-extension");
3854
- },
3855
- async restartBrowser() {
3856
- await closeAndRecreateRunner();
3857
- await runner.openBrowser();
3858
- }
3859
- };
3860
- return server2;
3861
- });
3862
- const server = wxt.server;
3863
- let [runner, builderServer] = await Promise.all([
3864
- createExtensionRunner(),
3865
- wxt.builder.createServer(server)
3866
- ]);
3867
- const buildAndOpenBrowser = async () => {
3868
- server.currentOutput = await internalBuild();
3869
- try {
3870
- server.watcher.add(getExternalOutputDependencies(server));
3871
- } catch (err) {
3872
- wxt.config.logger.warn("Failed to register additional file paths:", err);
3873
- }
3874
- await runner.openBrowser();
3875
- };
3876
- const closeAndRecreateRunner = async () => {
3877
- await runner.closeBrowser();
3878
- await wxt.reloadConfig();
3879
- runner = await createExtensionRunner();
3880
- };
3881
- server.ws.on("wxt:background-initialized", () => {
3882
- if (server.currentOutput == null) return;
3883
- reloadContentScripts(server.currentOutput.steps, server);
3884
- });
3885
- const reloadOnChange = createFileReloader(server);
3886
- server.watcher.on("all", reloadOnChange);
3887
- return server;
3888
- }
3889
- function createFileReloader(server) {
3890
- const fileChangedMutex = new Mutex();
3891
- const changeQueue = [];
3892
- return async (event, path11) => {
3893
- await wxt.reloadConfig();
3894
- if (path11.startsWith(wxt.config.outBaseDir)) return;
3895
- if (path11.startsWith(wxt.config.wxtDir)) return;
3896
- changeQueue.push([event, path11]);
3897
- await fileChangedMutex.runExclusive(async () => {
3898
- if (server.currentOutput == null) return;
3899
- const fileChanges = changeQueue.splice(0, changeQueue.length).map(([_, file]) => file);
3900
- if (fileChanges.length === 0) return;
3901
- const changes = detectDevChanges(fileChanges, server.currentOutput);
3902
- if (changes.type === "no-change") return;
3903
- if (changes.type === "full-restart") {
3904
- wxt.logger.info("Config changed, restarting server...");
3905
- server.restart();
3906
- return;
3907
- }
3908
- if (changes.type === "browser-restart") {
3909
- wxt.logger.info("Runner config changed, restarting browser...");
3910
- server.restartBrowser();
3911
- return;
3912
- }
3913
- wxt.logger.info(
3914
- `Changed: ${Array.from(new Set(fileChanges)).map((file) => pc7.dim(relative10(wxt.config.root, file))).join(", ")}`
3915
- );
3916
- const allEntrypoints = await findEntrypoints();
3917
- try {
3918
- const { output: newOutput } = await rebuild(
3919
- allEntrypoints,
3920
- // TODO: this excludes new entrypoints, so they're not built until the dev command is restarted
3921
- changes.rebuildGroups,
3922
- changes.cachedOutput
3923
- );
3924
- server.currentOutput = newOutput;
3925
- switch (changes.type) {
3926
- case "extension-reload":
3927
- server.reloadExtension();
3928
- wxt.logger.success(`Reloaded extension`);
3929
- break;
3930
- case "html-reload":
3931
- const { reloadedNames } = reloadHtmlPages(
3932
- changes.rebuildGroups,
3933
- server
3934
- );
3935
- wxt.logger.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
3936
- break;
3937
- case "content-script-reload":
3938
- reloadContentScripts(changes.changedSteps, server);
3939
- const rebuiltNames = changes.rebuildGroups.flat().map((entry) => entry.name);
3940
- wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
3941
- break;
3942
- }
3943
- } catch (err) {
3944
- }
3945
- });
3946
- };
3947
- }
3948
- function reloadContentScripts(steps, server) {
3949
- if (wxt.config.manifestVersion === 3) {
3950
- steps.forEach((step) => {
3951
- if (server.currentOutput == null) return;
3952
- const entry = step.entrypoints;
3953
- if (Array.isArray(entry) || entry.type !== "content-script") return;
3954
- const js = getContentScriptJs(wxt.config, entry);
3955
- const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
3956
- const css = getContentScriptCssFiles([entry], cssMap);
3957
- server.reloadContentScript({
3958
- registration: entry.options.registration,
3959
- contentScript: mapWxtOptionsToRegisteredContentScript(
3960
- entry.options,
3961
- js,
3962
- css
3963
- )
3964
- });
3965
- });
3966
- } else {
3967
- server.reloadExtension();
3968
- }
3969
- }
3970
- function reloadHtmlPages(groups, server) {
3971
- const htmlEntries = groups.flat().filter(isHtmlEntrypoint);
3972
- htmlEntries.forEach((entry) => {
3973
- const path11 = getEntrypointBundlePath(entry, wxt.config.outDir, ".html");
3974
- server.reloadPage(path11);
3975
- });
3976
- return {
3977
- reloadedNames: htmlEntries.map((entry) => entry.name)
3978
- };
3979
- }
3980
- function getFilenameList(names) {
3981
- return names.map((name) => {
3982
- return pc7.cyan(name);
3983
- }).join(pc7.dim(", "));
3984
- }
3985
- function getExternalOutputDependencies(server) {
3986
- return server.currentOutput?.steps.flatMap((step, i) => {
3987
- if (Array.isArray(step.entrypoints) && i === 0) {
3988
- return [];
3989
- }
3990
- return step.chunks.flatMap((chunk) => {
3991
- if (chunk.type === "asset") return [];
3992
- return chunk.moduleIds;
3993
- });
3994
- }).filter(
3995
- (file) => !file.includes("node_modules") && !file.startsWith("\0")
3996
- ).map(unnormalizePath).filter((file) => !file.startsWith(wxt.config.root)) ?? [];
3997
- }
3998
-
3999
- // src/core/initialize.ts
4000
- import prompts from "prompts";
4001
- import { downloadTemplate } from "giget";
4002
- import fs15 from "fs-extra";
4003
- import path9 from "node:path";
4004
- import pc8 from "picocolors";
4005
- async function initialize(options) {
4006
- consola.info("Initalizing new project");
4007
- const templates = await listTemplates();
4008
- const defaultTemplate = templates.find(
4009
- (template) => template.name === options.template?.toLowerCase().trim()
4010
- );
4011
- const input = await prompts(
4012
- [
4013
- {
4014
- name: "directory",
4015
- type: () => options.directory == null ? "text" : void 0,
4016
- message: "Project Directory",
4017
- initial: options.directory
4018
- },
4019
- {
4020
- name: "template",
4021
- type: () => defaultTemplate == null ? "select" : void 0,
4022
- message: "Choose a template",
4023
- choices: templates.map((template) => ({
4024
- title: TEMPLATE_COLORS[template.name]?.(template.name) ?? template.name,
4025
- value: template
4026
- }))
4027
- },
4028
- {
4029
- name: "packageManager",
4030
- type: () => options.packageManager == null ? "select" : void 0,
4031
- message: "Package Manager",
4032
- choices: [
4033
- { title: pc8.red("npm"), value: "npm" },
4034
- { title: pc8.yellow("pnpm"), value: "pnpm" },
4035
- { title: pc8.cyan("yarn"), value: "yarn" },
4036
- {
4037
- title: `${pc8.magenta("bun")}${pc8.gray(" (experimental)")}`,
4038
- value: "bun"
4039
- }
4040
- ]
4041
- }
4042
- ],
4043
- {
4044
- onCancel: () => process.exit(1)
4045
- }
4046
- );
4047
- input.directory ??= options.directory;
4048
- input.template ??= defaultTemplate;
4049
- input.packageManager ??= options.packageManager;
4050
- const isExists = await fs15.pathExists(input.directory);
4051
- if (isExists) {
4052
- const isEmpty = (await fs15.readdir(input.directory)).length === 0;
4053
- if (!isEmpty) {
4054
- consola.error(
4055
- `The directory ${path9.resolve(input.directory)} is not empty. Aborted.`
4056
- );
4057
- process.exit(1);
4058
- }
4059
- }
4060
- await cloneProject(input);
4061
- const cdPath = path9.relative(process.cwd(), path9.resolve(input.directory));
4062
- console.log();
4063
- consola.log(
4064
- `\u2728 WXT project created with the ${TEMPLATE_COLORS[input.template.name]?.(input.template.name) ?? input.template.name} template.`
4065
- );
4066
- console.log();
4067
- consola.log("Next steps:");
4068
- let step = 0;
4069
- if (cdPath !== "") consola.log(` ${++step}.`, pc8.cyan(`cd ${cdPath}`));
4070
- consola.log(` ${++step}.`, pc8.cyan(`${input.packageManager} install`));
4071
- console.log();
4072
- }
4073
- async function listTemplates() {
4074
- try {
4075
- const res = await fetch("https://ungh.cc/repos/wxt-dev/wxt/files/main");
4076
- if (res.status >= 300)
4077
- throw Error(`Request failed with status ${res.status} ${res.statusText}`);
4078
- const data = await res.json();
4079
- return data.files.map((item) => item.path.match(/templates\/(.+)\/package\.json/)?.[1]).filter((name) => name != null).map((name) => ({ name, path: `templates/${name}` })).sort((l, r) => {
4080
- const lWeight = TEMPLATE_SORT_WEIGHT[l.name] ?? Number.MAX_SAFE_INTEGER;
4081
- const rWeight = TEMPLATE_SORT_WEIGHT[r.name] ?? Number.MAX_SAFE_INTEGER;
4082
- const diff = lWeight - rWeight;
4083
- if (diff !== 0) return diff;
4084
- return l.name.localeCompare(r.name);
4085
- });
4086
- } catch (err) {
4087
- consola.error(err);
4088
- throw Error(`Failed to load templates`);
4089
- }
4090
- }
4091
- async function cloneProject({
4092
- directory,
4093
- template,
4094
- packageManager
4095
- }) {
4096
- const { default: ora } = await import("ora");
4097
- const spinner = ora("Downloading template").start();
4098
- try {
4099
- await downloadTemplate(`gh:wxt-dev/wxt/${template.path}`, {
4100
- dir: directory,
4101
- force: true
4102
- });
4103
- await fs15.move(
4104
- path9.join(directory, "_gitignore"),
4105
- path9.join(directory, ".gitignore")
4106
- ).catch(
4107
- (err) => consola.warn("Failed to move _gitignore to .gitignore:", err)
4108
- );
4109
- spinner.succeed();
4110
- } catch (err) {
4111
- spinner.fail();
4112
- throw Error(`Failed to setup new project: ${JSON.stringify(err, null, 2)}`);
4113
- }
4114
- }
4115
- var TEMPLATE_COLORS = {
4116
- vanilla: pc8.blue,
4117
- vue: pc8.green,
4118
- react: pc8.cyan,
4119
- svelte: pc8.red,
4120
- solid: pc8.blue
4121
- };
4122
- var TEMPLATE_SORT_WEIGHT = {
4123
- vanilla: 0,
4124
- vue: 1,
4125
- react: 2
4126
- };
4127
-
4128
- // src/core/prepare.ts
4129
- async function prepare(config) {
4130
- await registerWxt("build", config);
4131
- wxt.logger.info("Generating types...");
4132
- const entrypoints = await findEntrypoints();
4133
- await generateTypesDir(entrypoints);
4134
- }
4135
-
4136
- // src/core/zip.ts
4137
- import path10 from "node:path";
4138
- import fs16 from "fs-extra";
4139
- import { minimatch as minimatch2 } from "minimatch";
4140
- import JSZip from "jszip";
4141
- import glob7 from "fast-glob";
4142
- async function zip(config) {
4143
- await registerWxt("build", config);
4144
- const output = await internalBuild();
4145
- const start = Date.now();
4146
- wxt.logger.info("Zipping extension...");
4147
- const zipFiles = [];
4148
- const projectName = wxt.config.zip.name ?? kebabCaseAlphanumeric(
4149
- (await getPackageJson())?.name || path10.dirname(process.cwd())
4150
- );
4151
- const applyTemplate = (template) => template.replaceAll("{{name}}", projectName).replaceAll("{{browser}}", wxt.config.browser).replaceAll(
4152
- "{{version}}",
4153
- output.manifest.version_name ?? output.manifest.version
4154
- ).replaceAll("{{mode}}", wxt.config.mode).replaceAll("{{manifestVersion}}", `mv${wxt.config.manifestVersion}`);
4155
- await fs16.ensureDir(wxt.config.outBaseDir);
4156
- const outZipFilename = applyTemplate(wxt.config.zip.artifactTemplate);
4157
- const outZipPath = path10.resolve(wxt.config.outBaseDir, outZipFilename);
4158
- await zipDir(wxt.config.outDir, outZipPath);
4159
- zipFiles.push(outZipPath);
4160
- if (wxt.config.browser === "firefox") {
4161
- const { overrides, files: downloadedPackages } = await downloadPrivatePackages();
4162
- const sourcesZipFilename = applyTemplate(wxt.config.zip.sourcesTemplate);
4163
- const sourcesZipPath = path10.resolve(
4164
- wxt.config.outBaseDir,
4165
- sourcesZipFilename
4166
- );
4167
- await zipDir(wxt.config.zip.sourcesRoot, sourcesZipPath, {
4168
- include: wxt.config.zip.includeSources,
4169
- exclude: wxt.config.zip.excludeSources,
4170
- transform(absolutePath, zipPath, content) {
4171
- if (zipPath.endsWith("package.json")) {
4172
- return addOverridesToPackageJson(absolutePath, content, overrides);
4173
- }
4174
- },
4175
- additionalFiles: downloadedPackages
4176
- });
4177
- zipFiles.push(sourcesZipPath);
4178
- }
4179
- await printFileList(
4180
- wxt.logger.success,
4181
- `Zipped extension in ${formatDuration(Date.now() - start)}`,
4182
- wxt.config.outBaseDir,
4183
- zipFiles
4184
- );
4185
- return zipFiles;
4186
- }
4187
- async function zipDir(directory, outputPath, options) {
4188
- const archive = new JSZip();
4189
- const files = (await glob7("**/*", {
4190
- cwd: directory,
4191
- // Ignore node_modules, otherwise this glob step takes forever
4192
- ignore: ["**/node_modules"],
4193
- onlyFiles: true
4194
- // TODO: Fix #738
4195
- // dot: true,
4196
- })).filter((relativePath) => {
4197
- return options?.include?.some((pattern) => minimatch2(relativePath, pattern)) || !options?.exclude?.some((pattern) => minimatch2(relativePath, pattern));
4198
- });
4199
- const filesToZip = [
4200
- ...files,
4201
- ...(options?.additionalFiles ?? []).map(
4202
- (file) => path10.relative(directory, file)
4203
- )
4204
- ];
4205
- for (const file of filesToZip) {
4206
- const absolutePath = path10.resolve(directory, file);
4207
- if (file.endsWith(".json")) {
4208
- const content = await fs16.readFile(absolutePath, "utf-8");
4209
- archive.file(
4210
- file,
4211
- await options?.transform?.(absolutePath, file, content) || content
4212
- );
4213
- } else {
4214
- const content = await fs16.readFile(absolutePath);
4215
- archive.file(file, content);
4216
- }
4217
- }
4218
- await options?.additionalWork?.(archive);
4219
- await new Promise(
4220
- (resolve15, reject) => archive.generateNodeStream({
4221
- type: "nodebuffer",
4222
- ...wxt.config.zip.compressionLevel === 0 ? { compression: "STORE" } : {
4223
- compression: "DEFLATE",
4224
- compressionOptions: { level: wxt.config.zip.compressionLevel }
4225
- }
4226
- }).pipe(fs16.createWriteStream(outputPath)).on("error", reject).on("close", resolve15)
4227
- );
4228
- }
4229
- async function downloadPrivatePackages() {
4230
- const overrides = {};
4231
- const files = [];
4232
- if (wxt.config.zip.downloadPackages.length > 0) {
4233
- const _downloadPackages = new Set(wxt.config.zip.downloadPackages);
4234
- const allPackages = await wxt.pm.listDependencies({
4235
- all: true,
4236
- cwd: wxt.config.root
4237
- });
4238
- const downloadPackages = allPackages.filter(
4239
- (pkg) => _downloadPackages.has(pkg.name)
4240
- );
4241
- for (const pkg of downloadPackages) {
4242
- wxt.logger.info(`Downloading package: ${pkg.name}@${pkg.version}`);
4243
- const id = `${pkg.name}@${pkg.version}`;
4244
- const tgzPath = await wxt.pm.downloadDependency(
4245
- id,
4246
- wxt.config.zip.downloadedPackagesDir
4247
- );
4248
- files.push(tgzPath);
4249
- overrides[id] = tgzPath;
4250
- }
4251
- }
4252
- return { overrides, files };
4253
- }
4254
- function addOverridesToPackageJson(absolutePackageJsonPath, content, overrides) {
4255
- if (Object.keys(overrides).length === 0) return content;
4256
- const packageJsonDir = path10.dirname(absolutePackageJsonPath);
4257
- const oldPackage = JSON.parse(content);
4258
- const newPackage = {
4259
- ...oldPackage,
4260
- [wxt.pm.overridesKey]: { ...oldPackage[wxt.pm.overridesKey] }
4261
- };
4262
- Object.entries(overrides).forEach(([key, absolutePath]) => {
4263
- newPackage[wxt.pm.overridesKey][key] = "file://./" + normalizePath(path10.relative(packageJsonDir, absolutePath));
4264
- });
4265
- return JSON.stringify(newPackage, null, 2);
4266
- }
4267
-
4268
- // src/cli/cli-utils.ts
4269
- function wrapAction(cb, options) {
4270
- return async (...args) => {
4271
- const isDebug = !!args.find((arg) => arg?.debug);
4272
- if (isDebug) {
4273
- consola.level = LogLevels.debug;
4274
- }
4275
- const startTime = Date.now();
4276
- try {
4277
- printHeader();
4278
- const status = await cb(...args);
4279
- if (!status?.isOngoing && !options?.disableFinishedLog)
4280
- consola.success(
4281
- `Finished in ${formatDuration(Date.now() - startTime)}`
4282
- );
4283
- } catch (err) {
4284
- consola.fail(
4285
- `Command failed after ${formatDuration(Date.now() - startTime)}`
4286
- );
4287
- if (err instanceof ValidationError) {
4288
- } else {
4289
- consola.error(err);
4290
- }
4291
- process.exit(1);
4292
- }
4293
- };
4294
- }
4295
- function getArrayFromFlags(flags, name) {
4296
- const array = toArray(flags[name]);
4297
- const result = filterTruthy(array);
4298
- return result.length ? result : void 0;
4299
- }
4300
- var aliasCommandNames = /* @__PURE__ */ new Set();
4301
- function createAliasedCommand(base, name, alias, bin, docsUrl) {
4302
- const aliasedCommand = base.command(name, `Alias for ${alias} (${docsUrl})`).allowUnknownOptions().action(async () => {
4303
- try {
4304
- await registerWxt("build");
4305
- const args = process.argv.slice(
4306
- process.argv.indexOf(aliasedCommand.name) + 1
4307
- );
4308
- const { execa } = await import("./execa-QLUM2B3W.js");
4309
- await execa(bin, args, {
4310
- stdio: "inherit"
4311
- });
4312
- } catch {
4313
- process.exit(1);
4314
- }
4315
- });
4316
- aliasCommandNames.add(aliasedCommand.name);
4317
- }
4318
- function isAliasedCommand(command) {
4319
- return !!command && aliasCommandNames.has(command.name);
4320
- }
4321
-
4322
- // src/cli/commands.ts
4323
- var cli = cac("wxt");
4324
- cli.option("--debug", "enable debug mode");
4325
- 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("-p, --port <port>", "specify a port for the dev server").option(
4326
- "-e, --filter-entrypoint <entrypoint>",
4327
- "only build specific entrypoints",
4328
- {
4329
- type: []
4330
- }
4331
- ).option("--mv3", "target manifest v3").option("--mv2", "target manifest v2").action(
4332
- wrapAction(async (root, flags) => {
4333
- const server = await createServer({
4334
- root,
4335
- mode: flags.mode,
4336
- browser: flags.browser,
4337
- manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : void 0,
4338
- configFile: flags.config,
4339
- debug: flags.debug,
4340
- filterEntrypoints: getArrayFromFlags(flags, "filterEntrypoint"),
4341
- dev: flags.port == null ? void 0 : {
4342
- server: {
4343
- port: parseInt(flags.port)
4344
- }
4345
- }
4346
- });
4347
- await server.start();
4348
- return { isOngoing: true };
4349
- })
4350
- );
4351
- 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(
4352
- "-e, --filter-entrypoint <entrypoint>",
4353
- "only build specific entrypoints",
4354
- {
4355
- type: []
4356
- }
4357
- ).option("--mv3", "target manifest v3").option("--mv2", "target manifest v2").option("--analyze", "visualize extension bundle").option("--analyze-open", "automatically open stats.html in browser").action(
4358
- wrapAction(async (root, flags) => {
4359
- await build({
4360
- root,
4361
- mode: flags.mode,
4362
- browser: flags.browser,
4363
- manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : void 0,
4364
- configFile: flags.config,
4365
- debug: flags.debug,
4366
- analysis: flags.analyze ? {
4367
- enabled: true,
4368
- open: flags.analyzeOpen
4369
- } : void 0,
4370
- filterEntrypoints: getArrayFromFlags(flags, "filterEntrypoint")
4371
- });
4372
- })
4373
- );
4374
- cli.command("zip [root]", "build for production and zip output").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(
4375
- wrapAction(async (root, flags) => {
4376
- await zip({
4377
- root,
4378
- mode: flags.mode,
4379
- browser: flags.browser,
4380
- manifestVersion: flags.mv3 ? 3 : flags.mv2 ? 2 : void 0,
4381
- configFile: flags.config,
4382
- debug: flags.debug
4383
- });
4384
- })
4385
- );
4386
- cli.command("prepare [root]", "prepare typescript project").option("-c, --config <file>", "use specified config file").action(
4387
- wrapAction(async (root, flags) => {
4388
- await prepare({
4389
- root,
4390
- configFile: flags.config,
4391
- debug: flags.debug
4392
- });
4393
- })
4394
- );
4395
- cli.command("clean [root]", "clean generated files and caches").alias("cleanup").option("-c, --config <file>", "use specified config file").action(
4396
- wrapAction(async (root, flags) => {
4397
- await clean({ root, configFile: flags.config, debug: flags.debug });
4398
- })
4399
- );
4400
- cli.command("init [directory]", "initialize a new project").option("-t, --template <template>", "template to use").option("--pm <packageManager>", "which package manager to use").action(
4401
- wrapAction(
4402
- async (directory, flags) => {
4403
- await initialize({
4404
- directory,
4405
- template: flags.template,
4406
- packageManager: flags.pm
4407
- });
4408
- },
4409
- { disableFinishedLog: true }
4410
- )
4411
- );
4412
- createAliasedCommand(
4413
- cli,
4414
- "submit",
4415
- "publish-extension",
4416
- "wxt-publish-extension",
4417
- "https://www.npmjs.com/publish-browser-extension"
4418
- );
4419
- var commands_default = cli;
4420
-
4421
- // src/cli/index.ts
4422
- process.env.VITE_CJS_IGNORE_WARNING = "true";
4423
- commands_default.parse(process.argv, { run: false });
4424
- if (!isAliasedCommand(commands_default.matchedCommand)) {
4425
- commands_default.help();
4426
- commands_default.version(version);
4427
- commands_default.parse(process.argv, { run: false });
4428
- }
4429
- await commands_default.runMatchedCommand();