wxt 0.18.15 → 0.19.0

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