wxt 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/browser.js +3 -4
- package/dist/chunk-FNTE2L27.js +7 -0
- package/dist/chunk-NTYO6B6Y.js +2390 -0
- package/dist/chunk-YUG22S6W.js +38 -0
- package/dist/cli.cjs +3939 -3768
- package/dist/cli.d.cts +2 -0
- package/dist/client.d.ts +5 -171
- package/dist/client.js +128 -131
- package/dist/execa-WKZHVHC5.js +2043 -0
- package/dist/external-9107db91.d.ts +176 -0
- package/dist/external-cb0967d6.d.ts +572 -0
- package/dist/index.cjs +2741 -2364
- package/dist/index.d.cts +36 -583
- package/dist/index.d.ts +36 -583
- package/dist/index.js +330 -4479
- package/dist/sandbox.d.ts +2 -23
- package/dist/sandbox.js +1 -2
- package/dist/testing.cjs +141 -35
- package/dist/testing.d.cts +7 -273
- package/dist/testing.d.ts +7 -273
- package/dist/testing.js +10 -676
- package/dist/{virtual-modules → virtual}/background-entrypoint.js +6 -7
- package/dist/{virtual-modules → virtual}/content-script-entrypoint.js +4 -5
- package/dist/virtual/mock-browser.js +152 -0
- package/dist/{virtual-modules → virtual}/reload-html.js +2 -3
- package/dist/{virtual-modules → virtual}/unlisted-script-entrypoint.js +2 -3
- package/package.json +6 -5
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/virtual-modules/background-entrypoint.js.map +0 -1
- package/dist/virtual-modules/content-script-entrypoint.js.map +0 -1
- package/dist/virtual-modules/fake-browser.cjs +0 -31
- package/dist/virtual-modules/fake-browser.js +0 -8
- package/dist/virtual-modules/reload-html.js.map +0 -1
- package/dist/virtual-modules/unlisted-script-entrypoint.js.map +0 -1
|
@@ -0,0 +1,2390 @@
|
|
|
1
|
+
// package.json
|
|
2
|
+
var version = "0.8.5";
|
|
3
|
+
|
|
4
|
+
// src/core/utils/entrypoints.ts
|
|
5
|
+
import path, { relative, resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
// src/core/utils/paths.ts
|
|
8
|
+
import nodePath from "node:path";
|
|
9
|
+
import * as vite from "vite";
|
|
10
|
+
function normalizePath2(path7) {
|
|
11
|
+
return vite.normalizePath(path7);
|
|
12
|
+
}
|
|
13
|
+
function unnormalizePath(path7) {
|
|
14
|
+
return nodePath.normalize(path7);
|
|
15
|
+
}
|
|
16
|
+
var CSS_EXTENSIONS = ["css", "scss", "sass", "less", "styl", "stylus"];
|
|
17
|
+
var CSS_EXTENSIONS_PATTERN = `+(${CSS_EXTENSIONS.join("|")})`;
|
|
18
|
+
|
|
19
|
+
// src/core/utils/entrypoints.ts
|
|
20
|
+
function getEntrypointName(entrypointsDir, inputPath) {
|
|
21
|
+
const relativePath = path.relative(entrypointsDir, inputPath);
|
|
22
|
+
const name = relativePath.split(/[\.\/\\]/, 2)[0];
|
|
23
|
+
return name;
|
|
24
|
+
}
|
|
25
|
+
function getEntrypointOutputFile(entrypoint, ext) {
|
|
26
|
+
return resolve(entrypoint.outputDir, `${entrypoint.name}${ext}`);
|
|
27
|
+
}
|
|
28
|
+
function getEntrypointBundlePath(entrypoint, outDir, ext) {
|
|
29
|
+
return normalizePath2(
|
|
30
|
+
relative(outDir, getEntrypointOutputFile(entrypoint, ext))
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
function resolvePerBrowserOption(option, browser) {
|
|
34
|
+
if (typeof option === "object" && !Array.isArray(option))
|
|
35
|
+
return option[browser];
|
|
36
|
+
return option;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/core/utils/time.ts
|
|
40
|
+
function formatDuration(duration) {
|
|
41
|
+
if (duration < 1e3)
|
|
42
|
+
return `${duration} ms`;
|
|
43
|
+
if (duration < 1e4)
|
|
44
|
+
return `${(duration / 1e3).toFixed(3)} s`;
|
|
45
|
+
if (duration < 6e4)
|
|
46
|
+
return `${(duration / 1e3).toFixed(1)} s`;
|
|
47
|
+
return `${(duration / 1e3).toFixed(0)} s`;
|
|
48
|
+
}
|
|
49
|
+
function withTimeout(promise, duration) {
|
|
50
|
+
return new Promise((res, rej) => {
|
|
51
|
+
const timeout = setTimeout(() => {
|
|
52
|
+
rej(`Promise timed out after ${duration}ms`);
|
|
53
|
+
}, duration);
|
|
54
|
+
promise.then(res).catch(rej).finally(() => clearTimeout(timeout));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/core/utils/network.ts
|
|
59
|
+
import dns from "node:dns";
|
|
60
|
+
function isOffline() {
|
|
61
|
+
const isOffline2 = new Promise((res) => {
|
|
62
|
+
dns.resolve("google.com", (err) => {
|
|
63
|
+
if (err == null) {
|
|
64
|
+
res(false);
|
|
65
|
+
} else {
|
|
66
|
+
res(true);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return withTimeout(isOffline2, 1e3).catch(() => true);
|
|
71
|
+
}
|
|
72
|
+
async function isOnline() {
|
|
73
|
+
const offline = await isOffline();
|
|
74
|
+
return !offline;
|
|
75
|
+
}
|
|
76
|
+
async function fetchCached(url, config) {
|
|
77
|
+
let content = "";
|
|
78
|
+
if (await isOnline()) {
|
|
79
|
+
const res = await fetch(url);
|
|
80
|
+
if (res.status < 300) {
|
|
81
|
+
content = await res.text();
|
|
82
|
+
await config.fsCache.set(url, content);
|
|
83
|
+
} else {
|
|
84
|
+
config.logger.debug(
|
|
85
|
+
`Failed to download "${url}", falling back to cache...`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!content)
|
|
90
|
+
content = await config.fsCache.get(url) ?? "";
|
|
91
|
+
if (!content)
|
|
92
|
+
throw Error(
|
|
93
|
+
`Offline and "${url}" has not been cached. Try again when online.`
|
|
94
|
+
);
|
|
95
|
+
return content;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/core/vite-plugins/download.ts
|
|
99
|
+
function download(config) {
|
|
100
|
+
return {
|
|
101
|
+
name: "wxt:download",
|
|
102
|
+
resolveId(id) {
|
|
103
|
+
if (id.startsWith("url:"))
|
|
104
|
+
return "\0" + id;
|
|
105
|
+
},
|
|
106
|
+
async load(id) {
|
|
107
|
+
if (!id.startsWith("\0url:"))
|
|
108
|
+
return;
|
|
109
|
+
const url = id.replace("\0url:", "");
|
|
110
|
+
return await fetchCached(url, config);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/core/vite-plugins/unimport.ts
|
|
116
|
+
import { createUnimport } from "unimport";
|
|
117
|
+
|
|
118
|
+
// src/core/utils/unimport.ts
|
|
119
|
+
import { mergeConfig } from "vite";
|
|
120
|
+
function getUnimportOptions(config) {
|
|
121
|
+
if (config.imports === false)
|
|
122
|
+
return false;
|
|
123
|
+
const defaultOptions = {
|
|
124
|
+
debugLog: config.logger.debug,
|
|
125
|
+
imports: [
|
|
126
|
+
{ name: "defineConfig", from: "wxt" },
|
|
127
|
+
{ name: "fakeBrowser", from: "wxt/testing" }
|
|
128
|
+
],
|
|
129
|
+
presets: [
|
|
130
|
+
{ package: "wxt/client" },
|
|
131
|
+
{ package: "wxt/browser" },
|
|
132
|
+
{ package: "wxt/sandbox" }
|
|
133
|
+
],
|
|
134
|
+
warn: config.logger.warn,
|
|
135
|
+
dirs: ["components", "composables", "hooks", "utils"]
|
|
136
|
+
};
|
|
137
|
+
return mergeConfig(
|
|
138
|
+
defaultOptions,
|
|
139
|
+
config.imports
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/core/vite-plugins/unimport.ts
|
|
144
|
+
import { extname } from "path";
|
|
145
|
+
var ENABLED_EXTENSIONS = {
|
|
146
|
+
".js": true,
|
|
147
|
+
".jsx": true,
|
|
148
|
+
".ts": true,
|
|
149
|
+
".tsx": true,
|
|
150
|
+
".vue": true,
|
|
151
|
+
".svelte": true
|
|
152
|
+
};
|
|
153
|
+
function unimport(config) {
|
|
154
|
+
const options = getUnimportOptions(config);
|
|
155
|
+
if (options === false)
|
|
156
|
+
return [];
|
|
157
|
+
const unimport2 = createUnimport(options);
|
|
158
|
+
return {
|
|
159
|
+
name: "wxt:unimport",
|
|
160
|
+
async config() {
|
|
161
|
+
await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
|
|
162
|
+
},
|
|
163
|
+
async transform(code, id) {
|
|
164
|
+
const ext = extname(id);
|
|
165
|
+
if (ENABLED_EXTENSIONS[ext])
|
|
166
|
+
return unimport2.injectImports(code, id);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/core/vite-plugins/tsconfigPaths.ts
|
|
172
|
+
function tsconfigPaths(config) {
|
|
173
|
+
return {
|
|
174
|
+
name: "wxt:aliases",
|
|
175
|
+
async config() {
|
|
176
|
+
return {
|
|
177
|
+
resolve: {
|
|
178
|
+
alias: {
|
|
179
|
+
"@@": config.root,
|
|
180
|
+
"~~": config.root,
|
|
181
|
+
"@": config.srcDir,
|
|
182
|
+
"~": config.srcDir
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/core/utils/globals.ts
|
|
191
|
+
function getGlobals(config) {
|
|
192
|
+
return [
|
|
193
|
+
{
|
|
194
|
+
name: surroundInUnderscore("MANIFEST_VERSION"),
|
|
195
|
+
value: config.manifestVersion,
|
|
196
|
+
type: `2 | 3`
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: surroundInUnderscore("BROWSER"),
|
|
200
|
+
value: config.browser,
|
|
201
|
+
type: `string`
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: surroundInUnderscore("IS_CHROME"),
|
|
205
|
+
value: config.browser === "chrome",
|
|
206
|
+
type: `boolean`
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: surroundInUnderscore("IS_FIREFOX"),
|
|
210
|
+
value: config.browser === "firefox",
|
|
211
|
+
type: `boolean`
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: surroundInUnderscore("IS_SAFARI"),
|
|
215
|
+
value: config.browser === "safari",
|
|
216
|
+
type: `boolean`
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: surroundInUnderscore("IS_EDGE"),
|
|
220
|
+
value: config.browser === "edge",
|
|
221
|
+
type: `boolean`
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: surroundInUnderscore("IS_OPERA"),
|
|
225
|
+
value: config.browser === "opera",
|
|
226
|
+
type: `boolean`
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: surroundInUnderscore("COMMAND"),
|
|
230
|
+
value: config.command,
|
|
231
|
+
type: `"build" | "serve"`
|
|
232
|
+
}
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
function getEntrypointGlobals(config, entrypointName) {
|
|
236
|
+
return [
|
|
237
|
+
{
|
|
238
|
+
name: surroundInUnderscore("ENTRYPOINT"),
|
|
239
|
+
value: entrypointName,
|
|
240
|
+
type: `string`
|
|
241
|
+
}
|
|
242
|
+
];
|
|
243
|
+
}
|
|
244
|
+
function surroundInUnderscore(name) {
|
|
245
|
+
return `__${name}__`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/core/vite-plugins/globals.ts
|
|
249
|
+
function globals(config) {
|
|
250
|
+
return {
|
|
251
|
+
name: "wxt:globals",
|
|
252
|
+
config() {
|
|
253
|
+
const define = {};
|
|
254
|
+
for (const global of getGlobals(config)) {
|
|
255
|
+
define[global.name] = JSON.stringify(global.value);
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
define
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/core/vite-plugins/webextensionPolyfillAlias.ts
|
|
265
|
+
import path2 from "node:path";
|
|
266
|
+
function webextensionPolyfillAlias(config) {
|
|
267
|
+
return {
|
|
268
|
+
name: "wxt:webextension-polyfill-test-alias",
|
|
269
|
+
config() {
|
|
270
|
+
return {
|
|
271
|
+
resolve: {
|
|
272
|
+
alias: {
|
|
273
|
+
"webextension-polyfill": path2.resolve(
|
|
274
|
+
config.root,
|
|
275
|
+
"node_modules/wxt/dist/virtual/mock-browser"
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/core/vite-plugins/devHtmlPrerender.ts
|
|
285
|
+
import { parseHTML } from "linkedom";
|
|
286
|
+
import { dirname, isAbsolute, relative as relative2, resolve as resolve2 } from "path";
|
|
287
|
+
var reactRefreshPreamble = "";
|
|
288
|
+
function devHtmlPrerender(config) {
|
|
289
|
+
const htmlReloadId = "@wxt/reload-html";
|
|
290
|
+
const resolvedHtmlReloadId = resolve2(
|
|
291
|
+
config.root,
|
|
292
|
+
"node_modules/wxt/dist/virtual/reload-html.js"
|
|
293
|
+
);
|
|
294
|
+
const virtualReactRefreshId = "@wxt/virtual-react-refresh";
|
|
295
|
+
const resolvedVirtualReactRefreshId = "\0" + virtualReactRefreshId;
|
|
296
|
+
return [
|
|
297
|
+
{
|
|
298
|
+
apply: "build",
|
|
299
|
+
name: "wxt:dev-html-prerender",
|
|
300
|
+
config() {
|
|
301
|
+
return {
|
|
302
|
+
resolve: {
|
|
303
|
+
alias: {
|
|
304
|
+
[htmlReloadId]: resolvedHtmlReloadId
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
// Convert scripts like src="./main.tsx" -> src="http://localhost:3000/entrypoints/popup/main.tsx"
|
|
310
|
+
// before the paths are replaced with their bundled path
|
|
311
|
+
transform(code, id) {
|
|
312
|
+
const server = config.server;
|
|
313
|
+
if (config.command !== "serve" || server == null || !id.endsWith(".html"))
|
|
314
|
+
return;
|
|
315
|
+
const { document } = parseHTML(code);
|
|
316
|
+
const pointToDevServer = (querySelector, attr) => {
|
|
317
|
+
document.querySelectorAll(querySelector).forEach((element) => {
|
|
318
|
+
const src = element.getAttribute(attr);
|
|
319
|
+
if (!src)
|
|
320
|
+
return;
|
|
321
|
+
if (isAbsolute(src)) {
|
|
322
|
+
element.setAttribute(attr, server.origin + src);
|
|
323
|
+
} else if (src.startsWith(".")) {
|
|
324
|
+
const abs = resolve2(dirname(id), src);
|
|
325
|
+
const pathname = relative2(config.root, abs);
|
|
326
|
+
element.setAttribute(attr, `${server.origin}/${pathname}`);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
};
|
|
330
|
+
pointToDevServer("script[type=module]", "src");
|
|
331
|
+
pointToDevServer("link[rel=stylesheet]", "href");
|
|
332
|
+
const reloader = document.createElement("script");
|
|
333
|
+
reloader.src = htmlReloadId;
|
|
334
|
+
reloader.type = "module";
|
|
335
|
+
document.head.appendChild(reloader);
|
|
336
|
+
const newHtml = document.toString();
|
|
337
|
+
config.logger.debug("transform " + id);
|
|
338
|
+
config.logger.debug("Old HTML:\n" + code);
|
|
339
|
+
config.logger.debug("New HTML:\n" + newHtml);
|
|
340
|
+
return newHtml;
|
|
341
|
+
},
|
|
342
|
+
// Pass the HTML through the dev server to add dev-mode specific code
|
|
343
|
+
async transformIndexHtml(html, ctx) {
|
|
344
|
+
const server = config.server;
|
|
345
|
+
if (config.command !== "serve" || server == null)
|
|
346
|
+
return;
|
|
347
|
+
const originalUrl = `${server.origin}${ctx.path}`;
|
|
348
|
+
const name = getEntrypointName(config.entrypointsDir, ctx.filename);
|
|
349
|
+
const url = `${server.origin}/${name}.html`;
|
|
350
|
+
const serverHtml = await server.transformIndexHtml(
|
|
351
|
+
url,
|
|
352
|
+
html,
|
|
353
|
+
originalUrl
|
|
354
|
+
);
|
|
355
|
+
const { document } = parseHTML(serverHtml);
|
|
356
|
+
const reactRefreshScript = Array.from(
|
|
357
|
+
document.querySelectorAll("script[type=module]")
|
|
358
|
+
).find((script) => script.innerHTML.includes("@react-refresh"));
|
|
359
|
+
if (reactRefreshScript) {
|
|
360
|
+
reactRefreshPreamble = reactRefreshScript.innerHTML;
|
|
361
|
+
const virtualScript = document.createElement("script");
|
|
362
|
+
virtualScript.type = "module";
|
|
363
|
+
virtualScript.src = `${server.origin}/${virtualReactRefreshId}`;
|
|
364
|
+
reactRefreshScript.replaceWith(virtualScript);
|
|
365
|
+
}
|
|
366
|
+
const viteClientScript = document.querySelector(
|
|
367
|
+
"script[src='/@vite/client']"
|
|
368
|
+
);
|
|
369
|
+
if (viteClientScript) {
|
|
370
|
+
viteClientScript.src = `${server.origin}${viteClientScript.src}`;
|
|
371
|
+
}
|
|
372
|
+
const newHtml = document.toString();
|
|
373
|
+
config.logger.debug("transformIndexHtml " + ctx.filename);
|
|
374
|
+
config.logger.debug("Old HTML:\n" + html);
|
|
375
|
+
config.logger.debug("New HTML:\n" + newHtml);
|
|
376
|
+
return newHtml;
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: "wxt:virtualize-react-refresh",
|
|
381
|
+
apply: "serve",
|
|
382
|
+
resolveId(id) {
|
|
383
|
+
if (id === `/${virtualReactRefreshId}`) {
|
|
384
|
+
return resolvedVirtualReactRefreshId;
|
|
385
|
+
}
|
|
386
|
+
if (id.startsWith("/chunks/")) {
|
|
387
|
+
return "\0noop";
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
load(id) {
|
|
391
|
+
if (id === resolvedVirtualReactRefreshId) {
|
|
392
|
+
return reactRefreshPreamble;
|
|
393
|
+
}
|
|
394
|
+
if (id === "\0noop") {
|
|
395
|
+
return "";
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/core/vite-plugins/devServerGlobals.ts
|
|
403
|
+
function devServerGlobals(internalConfig) {
|
|
404
|
+
return {
|
|
405
|
+
name: "wxt:dev-server-globals",
|
|
406
|
+
config() {
|
|
407
|
+
if (internalConfig.server == null || internalConfig.command == "build")
|
|
408
|
+
return;
|
|
409
|
+
return {
|
|
410
|
+
define: {
|
|
411
|
+
__DEV_SERVER_PROTOCOL__: JSON.stringify("ws:"),
|
|
412
|
+
__DEV_SERVER_HOSTNAME__: JSON.stringify(
|
|
413
|
+
internalConfig.server.hostname
|
|
414
|
+
),
|
|
415
|
+
__DEV_SERVER_PORT__: JSON.stringify(internalConfig.server.port)
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/core/vite-plugins/multipageMove.ts
|
|
423
|
+
import { dirname as dirname2, extname as extname2, resolve as resolve3 } from "node:path";
|
|
424
|
+
import fs, { ensureDir } from "fs-extra";
|
|
425
|
+
function multipageMove(entrypoints, config) {
|
|
426
|
+
return {
|
|
427
|
+
name: "wxt:multipage-move",
|
|
428
|
+
async writeBundle(_, bundle) {
|
|
429
|
+
for (const oldBundlePath in bundle) {
|
|
430
|
+
const entrypoint = entrypoints.find(
|
|
431
|
+
(entry) => !!normalizePath2(entry.inputPath).endsWith(oldBundlePath)
|
|
432
|
+
);
|
|
433
|
+
if (entrypoint == null) {
|
|
434
|
+
config.logger.debug(
|
|
435
|
+
`No entrypoint found for ${oldBundlePath}, leaving in chunks directory`
|
|
436
|
+
);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const newBundlePath = getEntrypointBundlePath(
|
|
440
|
+
entrypoint,
|
|
441
|
+
config.outDir,
|
|
442
|
+
extname2(oldBundlePath)
|
|
443
|
+
);
|
|
444
|
+
if (newBundlePath === oldBundlePath) {
|
|
445
|
+
config.logger.debug(
|
|
446
|
+
"HTML file is already in the correct location",
|
|
447
|
+
oldBundlePath
|
|
448
|
+
);
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
const oldAbsPath = resolve3(config.outDir, oldBundlePath);
|
|
452
|
+
const newAbsPath = resolve3(config.outDir, newBundlePath);
|
|
453
|
+
await ensureDir(dirname2(newAbsPath));
|
|
454
|
+
await fs.move(oldAbsPath, newAbsPath, { overwrite: true });
|
|
455
|
+
const renamedChunk = {
|
|
456
|
+
...bundle[oldBundlePath],
|
|
457
|
+
fileName: newBundlePath
|
|
458
|
+
};
|
|
459
|
+
delete bundle[oldBundlePath];
|
|
460
|
+
bundle[newBundlePath] = renamedChunk;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// src/core/vite-plugins/virtualEntrypoint.ts
|
|
467
|
+
import fs2 from "fs-extra";
|
|
468
|
+
import { resolve as resolve4 } from "path";
|
|
469
|
+
function virtualEntrypoint(type, config) {
|
|
470
|
+
const virtualId = `virtual:wxt-${type}?`;
|
|
471
|
+
const resolvedVirtualId = `\0${virtualId}`;
|
|
472
|
+
return {
|
|
473
|
+
name: `wxt:virtual-entrypoint`,
|
|
474
|
+
resolveId(id) {
|
|
475
|
+
const index = id.indexOf(virtualId);
|
|
476
|
+
if (index === -1)
|
|
477
|
+
return;
|
|
478
|
+
const inputPath = normalizePath2(id.substring(index + virtualId.length));
|
|
479
|
+
return resolvedVirtualId + inputPath;
|
|
480
|
+
},
|
|
481
|
+
async load(id) {
|
|
482
|
+
if (!id.startsWith(resolvedVirtualId))
|
|
483
|
+
return;
|
|
484
|
+
const inputPath = id.replace(resolvedVirtualId, "");
|
|
485
|
+
const template = await fs2.readFile(
|
|
486
|
+
resolve4(
|
|
487
|
+
config.root,
|
|
488
|
+
`node_modules/wxt/dist/virtual/${type}-entrypoint.js`
|
|
489
|
+
),
|
|
490
|
+
"utf-8"
|
|
491
|
+
);
|
|
492
|
+
return template.replace(`virtual:user-${type}`, inputPath);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/core/vite-plugins/noopBackground.ts
|
|
498
|
+
function noopBackground() {
|
|
499
|
+
const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
|
|
500
|
+
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
501
|
+
return {
|
|
502
|
+
name: "wxt:noop-background",
|
|
503
|
+
resolveId(id) {
|
|
504
|
+
if (id === virtualModuleId)
|
|
505
|
+
return resolvedVirtualModuleId;
|
|
506
|
+
},
|
|
507
|
+
load(id) {
|
|
508
|
+
if (id === resolvedVirtualModuleId) {
|
|
509
|
+
return `import { defineBackground } from 'wxt/client';
|
|
510
|
+
export default defineBackground(() => void 0)`;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
|
|
516
|
+
|
|
517
|
+
// src/core/vite-plugins/cssEntrypoints.ts
|
|
518
|
+
function cssEntrypoints(entrypoint, config) {
|
|
519
|
+
return {
|
|
520
|
+
name: "wxt:css-entrypoint",
|
|
521
|
+
config() {
|
|
522
|
+
return {
|
|
523
|
+
build: {
|
|
524
|
+
rollupOptions: {
|
|
525
|
+
output: {
|
|
526
|
+
assetFileNames: () => getEntrypointBundlePath(entrypoint, config.outDir, ".css")
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
},
|
|
532
|
+
generateBundle(_, bundle) {
|
|
533
|
+
Object.keys(bundle).forEach((file) => {
|
|
534
|
+
if (file.endsWith(".js"))
|
|
535
|
+
delete bundle[file];
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/core/vite-plugins/bundleAnalysis.ts
|
|
542
|
+
import { visualizer } from "rollup-plugin-visualizer";
|
|
543
|
+
var increment = 0;
|
|
544
|
+
function bundleAnalysis() {
|
|
545
|
+
return visualizer({
|
|
546
|
+
emitFile: true,
|
|
547
|
+
template: "raw-data",
|
|
548
|
+
filename: `stats-${increment++}.json`
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/core/utils/arrays.ts
|
|
553
|
+
function every(array, predicate) {
|
|
554
|
+
for (let i = 0; i < array.length; i++)
|
|
555
|
+
if (!predicate(array[i], i))
|
|
556
|
+
return false;
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// src/core/utils/building/detect-dev-changes.ts
|
|
561
|
+
function detectDevChanges(changedFiles, currentOutput) {
|
|
562
|
+
if (currentOutput == null)
|
|
563
|
+
return { type: "no-change" };
|
|
564
|
+
const changedSteps = new Set(
|
|
565
|
+
changedFiles.flatMap(
|
|
566
|
+
(changedFile) => findEffectedSteps(changedFile, currentOutput)
|
|
567
|
+
)
|
|
568
|
+
);
|
|
569
|
+
if (changedSteps.size === 0)
|
|
570
|
+
return { type: "no-change" };
|
|
571
|
+
const unchangedOutput = {
|
|
572
|
+
manifest: currentOutput.manifest,
|
|
573
|
+
steps: [],
|
|
574
|
+
publicAssets: []
|
|
575
|
+
};
|
|
576
|
+
const changedOutput = {
|
|
577
|
+
manifest: currentOutput.manifest,
|
|
578
|
+
steps: [],
|
|
579
|
+
publicAssets: []
|
|
580
|
+
};
|
|
581
|
+
for (const step of currentOutput.steps) {
|
|
582
|
+
if (changedSteps.has(step)) {
|
|
583
|
+
changedOutput.steps.push(step);
|
|
584
|
+
} else {
|
|
585
|
+
unchangedOutput.steps.push(step);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
for (const asset of currentOutput.publicAssets) {
|
|
589
|
+
if (changedSteps.has(asset)) {
|
|
590
|
+
changedOutput.publicAssets.push(asset);
|
|
591
|
+
} else {
|
|
592
|
+
unchangedOutput.publicAssets.push(asset);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, ([_, file]) => file.endsWith(".html"));
|
|
596
|
+
if (isOnlyHtmlChanges) {
|
|
597
|
+
return {
|
|
598
|
+
type: "html-reload",
|
|
599
|
+
cachedOutput: unchangedOutput,
|
|
600
|
+
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
|
|
604
|
+
changedOutput.steps.flatMap((step) => step.entrypoints),
|
|
605
|
+
(entry) => entry.type === "content-script"
|
|
606
|
+
);
|
|
607
|
+
if (isOnlyContentScripts) {
|
|
608
|
+
return {
|
|
609
|
+
type: "content-script-reload",
|
|
610
|
+
cachedOutput: unchangedOutput,
|
|
611
|
+
changedSteps: changedOutput.steps,
|
|
612
|
+
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
type: "extension-reload",
|
|
617
|
+
cachedOutput: unchangedOutput,
|
|
618
|
+
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
function findEffectedSteps(changedFile, currentOutput) {
|
|
622
|
+
const changes = [];
|
|
623
|
+
const changedPath = normalizePath2(changedFile[1]);
|
|
624
|
+
const isChunkEffected = (chunk) => (
|
|
625
|
+
// If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
|
|
626
|
+
// fileName is normalized, relative bundle path
|
|
627
|
+
chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
|
|
628
|
+
// moduleIds are absolute, normalized paths
|
|
629
|
+
chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
|
|
630
|
+
);
|
|
631
|
+
for (const step of currentOutput.steps) {
|
|
632
|
+
const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
|
|
633
|
+
if (effectedChunk)
|
|
634
|
+
changes.push(step);
|
|
635
|
+
}
|
|
636
|
+
const effectedAsset = currentOutput.publicAssets.find(
|
|
637
|
+
(chunk) => isChunkEffected(chunk)
|
|
638
|
+
);
|
|
639
|
+
if (effectedAsset)
|
|
640
|
+
changes.push(effectedAsset);
|
|
641
|
+
return changes;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/core/utils/building/find-entrypoints.ts
|
|
645
|
+
import { relative as relative4, resolve as resolve12 } from "path";
|
|
646
|
+
import fs12 from "fs-extra";
|
|
647
|
+
import { minimatch } from "minimatch";
|
|
648
|
+
import { parseHTML as parseHTML2 } from "linkedom";
|
|
649
|
+
import JSON5 from "json5";
|
|
650
|
+
|
|
651
|
+
// src/core/utils/building/build-entrypoints.ts
|
|
652
|
+
import * as vite2 from "vite";
|
|
653
|
+
|
|
654
|
+
// src/core/utils/fs.ts
|
|
655
|
+
import fs3 from "fs-extra";
|
|
656
|
+
import glob from "fast-glob";
|
|
657
|
+
import path3 from "node:path";
|
|
658
|
+
async function writeFileIfDifferent(file, newContents) {
|
|
659
|
+
const existingContents = await fs3.readFile(file, "utf-8").catch(() => void 0);
|
|
660
|
+
if (existingContents !== newContents) {
|
|
661
|
+
await fs3.writeFile(file, newContents);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function getPublicFiles(config) {
|
|
665
|
+
if (!await fs3.exists(config.publicDir))
|
|
666
|
+
return [];
|
|
667
|
+
const files = await glob("**/*", { cwd: config.publicDir });
|
|
668
|
+
return files.map(unnormalizePath);
|
|
669
|
+
}
|
|
670
|
+
async function removeEmptyDirs(dir) {
|
|
671
|
+
const files = await fs3.readdir(dir);
|
|
672
|
+
for (const file of files) {
|
|
673
|
+
const filePath = path3.join(dir, file);
|
|
674
|
+
const stats = await fs3.stat(filePath);
|
|
675
|
+
if (stats.isDirectory()) {
|
|
676
|
+
await removeEmptyDirs(filePath);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
await fs3.rmdir(dir);
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/core/utils/building/build-entrypoints.ts
|
|
686
|
+
import fs4 from "fs-extra";
|
|
687
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
688
|
+
import pc from "picocolors";
|
|
689
|
+
async function buildEntrypoints(groups, config, spinner) {
|
|
690
|
+
const steps = [];
|
|
691
|
+
for (let i = 0; i < groups.length; i++) {
|
|
692
|
+
const group = groups[i];
|
|
693
|
+
spinner.text = pc.dim(`[${i + 1}/${groups.length}]`) + ` ${[group].flat().map((e) => e.name).join(pc.dim(", "))}`;
|
|
694
|
+
const step = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config);
|
|
695
|
+
steps.push(step);
|
|
696
|
+
}
|
|
697
|
+
const publicAssets = await copyPublicDirectory(config);
|
|
698
|
+
await removeEmptyDirs(config.outDir);
|
|
699
|
+
return { publicAssets, steps };
|
|
700
|
+
}
|
|
701
|
+
async function buildSingleEntrypoint(entrypoint, config) {
|
|
702
|
+
const isVirtual = [
|
|
703
|
+
"background",
|
|
704
|
+
"content-script",
|
|
705
|
+
"unlisted-script"
|
|
706
|
+
].includes(entrypoint.type);
|
|
707
|
+
const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
|
|
708
|
+
const plugins = [];
|
|
709
|
+
if (entrypoint.type === "content-script-style" || entrypoint.type === "unlisted-style") {
|
|
710
|
+
plugins.push(cssEntrypoints(entrypoint, config));
|
|
711
|
+
}
|
|
712
|
+
const libMode = {
|
|
713
|
+
plugins,
|
|
714
|
+
build: {
|
|
715
|
+
lib: {
|
|
716
|
+
entry,
|
|
717
|
+
formats: ["iife"],
|
|
718
|
+
name: "_",
|
|
719
|
+
fileName: entrypoint.name
|
|
720
|
+
},
|
|
721
|
+
rollupOptions: {
|
|
722
|
+
output: {
|
|
723
|
+
// There's only a single output for this build, so we use the desired bundle path for the
|
|
724
|
+
// entry output (like "content-scripts/overlay.js")
|
|
725
|
+
entryFileNames: getEntrypointBundlePath(
|
|
726
|
+
entrypoint,
|
|
727
|
+
config.outDir,
|
|
728
|
+
".js"
|
|
729
|
+
),
|
|
730
|
+
// Output content script CSS to `content-scripts/`, but all other scripts are written to
|
|
731
|
+
// `assets/`.
|
|
732
|
+
assetFileNames: ({ name }) => {
|
|
733
|
+
if (entrypoint.type === "content-script" && name?.endsWith("css")) {
|
|
734
|
+
return `content-scripts/${entrypoint.name}.[ext]`;
|
|
735
|
+
} else {
|
|
736
|
+
return `assets/${entrypoint.name}.[ext]`;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
define: {
|
|
743
|
+
// See https://github.com/aklinker1/vite-plugin-web-extension/issues/96
|
|
744
|
+
"process.env.NODE_ENV": JSON.stringify(config.mode)
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
for (const global of getEntrypointGlobals(config, entrypoint.name)) {
|
|
748
|
+
libMode.define[global.name] = JSON.stringify(global.value);
|
|
749
|
+
}
|
|
750
|
+
const entryConfig = vite2.mergeConfig(
|
|
751
|
+
libMode,
|
|
752
|
+
await config.vite(config.env)
|
|
753
|
+
);
|
|
754
|
+
const result = await vite2.build(entryConfig);
|
|
755
|
+
return {
|
|
756
|
+
entrypoints: entrypoint,
|
|
757
|
+
chunks: getBuildOutputChunks(result)
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
async function buildMultipleEntrypoints(entrypoints, config) {
|
|
761
|
+
const multiPage = {
|
|
762
|
+
plugins: [multipageMove(entrypoints, config)],
|
|
763
|
+
build: {
|
|
764
|
+
rollupOptions: {
|
|
765
|
+
input: entrypoints.reduce((input, entry) => {
|
|
766
|
+
input[entry.name] = entry.inputPath;
|
|
767
|
+
return input;
|
|
768
|
+
}, {}),
|
|
769
|
+
output: {
|
|
770
|
+
// Include a hash to prevent conflicts
|
|
771
|
+
chunkFileNames: "chunks/[name]-[hash].js",
|
|
772
|
+
// Include a hash to prevent conflicts
|
|
773
|
+
entryFileNames: "chunks/[name]-[hash].js",
|
|
774
|
+
// We can't control the "name", so we need a hash to prevent conflicts
|
|
775
|
+
assetFileNames: "assets/[name]-[hash].[ext]"
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
define: {}
|
|
780
|
+
};
|
|
781
|
+
for (const global of getEntrypointGlobals(config, "html")) {
|
|
782
|
+
multiPage.define[global.name] = JSON.stringify(global.value);
|
|
783
|
+
}
|
|
784
|
+
const entryConfig = vite2.mergeConfig(
|
|
785
|
+
multiPage,
|
|
786
|
+
await config.vite(config.env)
|
|
787
|
+
);
|
|
788
|
+
const result = await vite2.build(entryConfig);
|
|
789
|
+
return {
|
|
790
|
+
entrypoints,
|
|
791
|
+
chunks: getBuildOutputChunks(result)
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function getBuildOutputChunks(result) {
|
|
795
|
+
if ("on" in result)
|
|
796
|
+
throw Error("wxt does not support vite watch mode.");
|
|
797
|
+
if (Array.isArray(result))
|
|
798
|
+
return result.flatMap(({ output }) => output);
|
|
799
|
+
return result.output;
|
|
800
|
+
}
|
|
801
|
+
async function copyPublicDirectory(config) {
|
|
802
|
+
const files = await getPublicFiles(config);
|
|
803
|
+
if (files.length === 0)
|
|
804
|
+
return [];
|
|
805
|
+
const publicAssets = [];
|
|
806
|
+
for (const file of files) {
|
|
807
|
+
const srcPath = resolve5(config.publicDir, file);
|
|
808
|
+
const outPath = resolve5(config.outDir, file);
|
|
809
|
+
await fs4.ensureDir(dirname3(outPath));
|
|
810
|
+
await fs4.copyFile(srcPath, outPath);
|
|
811
|
+
publicAssets.push({
|
|
812
|
+
type: "asset",
|
|
813
|
+
fileName: file,
|
|
814
|
+
name: file,
|
|
815
|
+
needsCodeReference: false,
|
|
816
|
+
source: await fs4.readFile(srcPath)
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
return publicAssets;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// src/core/utils/building/generate-wxt-dir.ts
|
|
823
|
+
import { createUnimport as createUnimport2 } from "unimport";
|
|
824
|
+
import fs5 from "fs-extra";
|
|
825
|
+
import { relative as relative3, resolve as resolve6 } from "path";
|
|
826
|
+
import path4 from "node:path";
|
|
827
|
+
|
|
828
|
+
// src/core/utils/i18n.ts
|
|
829
|
+
var predefinedMessages = {
|
|
830
|
+
"@@extension_id": {
|
|
831
|
+
message: "<browser.runtime.id>",
|
|
832
|
+
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."
|
|
833
|
+
},
|
|
834
|
+
"@@ui_locale": {
|
|
835
|
+
message: "<browser.i18n.getUiLocale()>",
|
|
836
|
+
description: ""
|
|
837
|
+
},
|
|
838
|
+
"@@bidi_dir": {
|
|
839
|
+
message: "<ltr|rtl>",
|
|
840
|
+
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.'
|
|
841
|
+
},
|
|
842
|
+
"@@bidi_reversed_dir": {
|
|
843
|
+
message: "<rtl|ltr>",
|
|
844
|
+
description: `If the @@bidi_dir is "ltr", then this is "rtl"; otherwise, it's "ltr".`
|
|
845
|
+
},
|
|
846
|
+
"@@bidi_start_edge": {
|
|
847
|
+
message: "<left|right>",
|
|
848
|
+
description: `If the @@bidi_dir is "ltr", then this is "left"; otherwise, it's "right".`
|
|
849
|
+
},
|
|
850
|
+
"@@bidi_end_edge": {
|
|
851
|
+
message: "<right|left>",
|
|
852
|
+
description: `If the @@bidi_dir is "ltr", then this is "right"; otherwise, it's "left".`
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
function parseI18nMessages(messagesJson) {
|
|
856
|
+
return Object.entries({
|
|
857
|
+
...predefinedMessages,
|
|
858
|
+
...messagesJson
|
|
859
|
+
}).map(([name, details]) => ({
|
|
860
|
+
name,
|
|
861
|
+
...details
|
|
862
|
+
}));
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// src/core/utils/building/generate-wxt-dir.ts
|
|
866
|
+
async function generateTypesDir(entrypoints, config) {
|
|
867
|
+
await fs5.ensureDir(config.typesDir);
|
|
868
|
+
const references = [];
|
|
869
|
+
const imports = getUnimportOptions(config);
|
|
870
|
+
if (imports !== false) {
|
|
871
|
+
references.push(await writeImportsDeclarationFile(config, imports));
|
|
872
|
+
}
|
|
873
|
+
references.push(await writePathsDeclarationFile(entrypoints, config));
|
|
874
|
+
references.push(await writeI18nDeclarationFile(config));
|
|
875
|
+
references.push(await writeGlobalsDeclarationFile(config));
|
|
876
|
+
const mainReference = await writeMainDeclarationFile(references, config);
|
|
877
|
+
await writeTsConfigFile(mainReference, config);
|
|
878
|
+
}
|
|
879
|
+
async function writeImportsDeclarationFile(config, unimportOptions) {
|
|
880
|
+
const filePath = resolve6(config.typesDir, "imports.d.ts");
|
|
881
|
+
const unimport2 = createUnimport2(unimportOptions);
|
|
882
|
+
await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
|
|
883
|
+
await writeFileIfDifferent(
|
|
884
|
+
filePath,
|
|
885
|
+
["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
|
|
886
|
+
"\n"
|
|
887
|
+
) + "\n"
|
|
888
|
+
);
|
|
889
|
+
return filePath;
|
|
890
|
+
}
|
|
891
|
+
async function writePathsDeclarationFile(entrypoints, config) {
|
|
892
|
+
const filePath = resolve6(config.typesDir, "paths.d.ts");
|
|
893
|
+
const unions = entrypoints.map(
|
|
894
|
+
(entry) => getEntrypointBundlePath(
|
|
895
|
+
entry,
|
|
896
|
+
config.outDir,
|
|
897
|
+
entry.inputPath.endsWith(".html") ? ".html" : ".js"
|
|
898
|
+
)
|
|
899
|
+
).concat(await getPublicFiles(config)).map(normalizePath2).map((path7) => ` | "/${path7}"`).sort().join("\n");
|
|
900
|
+
const template = `// Generated by wxt
|
|
901
|
+
import "wxt/browser";
|
|
902
|
+
|
|
903
|
+
declare module "wxt/browser" {
|
|
904
|
+
type PublicPath =
|
|
905
|
+
{{ union }}
|
|
906
|
+
export interface WxtRuntime extends Runtime.Static {
|
|
907
|
+
getURL(path: PublicPath): string;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
`;
|
|
911
|
+
await writeFileIfDifferent(
|
|
912
|
+
filePath,
|
|
913
|
+
template.replace("{{ union }}", unions || " | never")
|
|
914
|
+
);
|
|
915
|
+
return filePath;
|
|
916
|
+
}
|
|
917
|
+
async function writeI18nDeclarationFile(config) {
|
|
918
|
+
const filePath = resolve6(config.typesDir, "i18n.d.ts");
|
|
919
|
+
const defaultLocale = config.manifest.default_locale;
|
|
920
|
+
const template = `// Generated by wxt
|
|
921
|
+
import "wxt/browser";
|
|
922
|
+
|
|
923
|
+
declare module "wxt/browser" {
|
|
924
|
+
/**
|
|
925
|
+
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
|
|
926
|
+
*/
|
|
927
|
+
interface GetMessageOptions {
|
|
928
|
+
/**
|
|
929
|
+
* See https://developer.chrome.com/docs/extensions/reference/i18n/#method-getMessage
|
|
930
|
+
*/
|
|
931
|
+
escapeLt?: boolean
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
export interface WxtI18n extends I18n.Static {
|
|
935
|
+
{{ overrides }}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
`;
|
|
939
|
+
let messages;
|
|
940
|
+
if (defaultLocale) {
|
|
941
|
+
const defaultLocalePath = path4.resolve(
|
|
942
|
+
config.publicDir,
|
|
943
|
+
"_locales",
|
|
944
|
+
defaultLocale,
|
|
945
|
+
"messages.json"
|
|
946
|
+
);
|
|
947
|
+
const content = JSON.parse(await fs5.readFile(defaultLocalePath, "utf-8"));
|
|
948
|
+
messages = parseI18nMessages(content);
|
|
949
|
+
} else {
|
|
950
|
+
messages = parseI18nMessages({});
|
|
951
|
+
}
|
|
952
|
+
const overrides = messages.map((message) => {
|
|
953
|
+
return ` /**
|
|
954
|
+
* ${message.description ?? "No message description."}
|
|
955
|
+
*
|
|
956
|
+
* "${message.message}"
|
|
957
|
+
*/
|
|
958
|
+
getMessage(
|
|
959
|
+
messageName: "${message.name}",
|
|
960
|
+
substitutions?: string | string[],
|
|
961
|
+
options?: GetMessageOptions,
|
|
962
|
+
): string;`;
|
|
963
|
+
});
|
|
964
|
+
await writeFileIfDifferent(
|
|
965
|
+
filePath,
|
|
966
|
+
template.replace("{{ overrides }}", overrides.join("\n"))
|
|
967
|
+
);
|
|
968
|
+
return filePath;
|
|
969
|
+
}
|
|
970
|
+
async function writeGlobalsDeclarationFile(config) {
|
|
971
|
+
const filePath = resolve6(config.typesDir, "globals.d.ts");
|
|
972
|
+
const globals2 = [...getGlobals(config), ...getEntrypointGlobals(config, "")];
|
|
973
|
+
await writeFileIfDifferent(
|
|
974
|
+
filePath,
|
|
975
|
+
[
|
|
976
|
+
"// Generated by wxt",
|
|
977
|
+
"export {}",
|
|
978
|
+
"declare global {",
|
|
979
|
+
...globals2.map((global) => ` const ${global.name}: ${global.type};`),
|
|
980
|
+
"}"
|
|
981
|
+
].join("\n") + "\n"
|
|
982
|
+
);
|
|
983
|
+
return filePath;
|
|
984
|
+
}
|
|
985
|
+
async function writeMainDeclarationFile(references, config) {
|
|
986
|
+
const dir = config.wxtDir;
|
|
987
|
+
const filePath = resolve6(dir, "wxt.d.ts");
|
|
988
|
+
await writeFileIfDifferent(
|
|
989
|
+
filePath,
|
|
990
|
+
[
|
|
991
|
+
"// Generated by wxt",
|
|
992
|
+
`/// <reference types="vite/client" />`,
|
|
993
|
+
...references.map(
|
|
994
|
+
(ref) => `/// <reference types="./${normalizePath2(relative3(dir, ref))}" />`
|
|
995
|
+
)
|
|
996
|
+
].join("\n") + "\n"
|
|
997
|
+
);
|
|
998
|
+
return filePath;
|
|
999
|
+
}
|
|
1000
|
+
async function writeTsConfigFile(mainReference, config) {
|
|
1001
|
+
const dir = config.wxtDir;
|
|
1002
|
+
const rootPath = normalizePath2(relative3(dir, config.root));
|
|
1003
|
+
const srcPath = normalizePath2(relative3(dir, config.srcDir));
|
|
1004
|
+
await writeFileIfDifferent(
|
|
1005
|
+
resolve6(dir, "tsconfig.json"),
|
|
1006
|
+
`{
|
|
1007
|
+
"compilerOptions": {
|
|
1008
|
+
"target": "ESNext",
|
|
1009
|
+
"module": "ESNext",
|
|
1010
|
+
"moduleResolution": "Bundler",
|
|
1011
|
+
"noEmit": true,
|
|
1012
|
+
"esModuleInterop": true,
|
|
1013
|
+
"forceConsistentCasingInFileNames": true,
|
|
1014
|
+
"resolveJsonModule": true,
|
|
1015
|
+
"strict": true,
|
|
1016
|
+
"lib": ["DOM", "WebWorker"],
|
|
1017
|
+
"skipLibCheck": true,
|
|
1018
|
+
"paths": {
|
|
1019
|
+
"@": ["${srcPath}"],
|
|
1020
|
+
"@/*": ["${srcPath}/*"],
|
|
1021
|
+
"~": ["${srcPath}"],
|
|
1022
|
+
"~/*": ["${srcPath}/*"],
|
|
1023
|
+
"@@": ["${rootPath}"],
|
|
1024
|
+
"@@/*": ["${rootPath}/*"],
|
|
1025
|
+
"~~": ["${rootPath}"],
|
|
1026
|
+
"~~/*": ["${rootPath}/*"]
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
"include": [
|
|
1030
|
+
"${normalizePath2(relative3(dir, config.root))}/**/*",
|
|
1031
|
+
"./${normalizePath2(relative3(dir, mainReference))}"
|
|
1032
|
+
],
|
|
1033
|
+
"exclude": ["${normalizePath2(relative3(dir, config.outBaseDir))}"]
|
|
1034
|
+
}`
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// src/core/utils/building/get-internal-config.ts
|
|
1039
|
+
import { loadConfig } from "c12";
|
|
1040
|
+
import path5 from "node:path";
|
|
1041
|
+
import * as vite3 from "vite";
|
|
1042
|
+
|
|
1043
|
+
// src/core/utils/cache.ts
|
|
1044
|
+
import fs6, { ensureDir as ensureDir2 } from "fs-extra";
|
|
1045
|
+
import { dirname as dirname4, resolve as resolve7 } from "path";
|
|
1046
|
+
function createFsCache(wxtDir) {
|
|
1047
|
+
const getPath = (key) => resolve7(wxtDir, "cache", encodeURIComponent(key));
|
|
1048
|
+
return {
|
|
1049
|
+
async set(key, value) {
|
|
1050
|
+
const path7 = getPath(key);
|
|
1051
|
+
await ensureDir2(dirname4(path7));
|
|
1052
|
+
await writeFileIfDifferent(path7, value);
|
|
1053
|
+
},
|
|
1054
|
+
async get(key) {
|
|
1055
|
+
const path7 = getPath(key);
|
|
1056
|
+
try {
|
|
1057
|
+
return await fs6.readFile(path7, "utf-8");
|
|
1058
|
+
} catch {
|
|
1059
|
+
return void 0;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// src/core/utils/building/get-internal-config.ts
|
|
1066
|
+
import consola, { LogLevels } from "consola";
|
|
1067
|
+
async function getInternalConfig(inlineConfig, command) {
|
|
1068
|
+
let userConfig = {};
|
|
1069
|
+
let userConfigMetadata;
|
|
1070
|
+
if (inlineConfig.configFile !== false) {
|
|
1071
|
+
const { config: loadedConfig, ...metadata } = await loadConfig({
|
|
1072
|
+
name: "wxt",
|
|
1073
|
+
cwd: inlineConfig.root ?? process.cwd(),
|
|
1074
|
+
rcFile: false
|
|
1075
|
+
});
|
|
1076
|
+
userConfig = loadedConfig ?? {};
|
|
1077
|
+
userConfigMetadata = metadata;
|
|
1078
|
+
}
|
|
1079
|
+
const mergedConfig = mergeInlineConfig(inlineConfig, userConfig);
|
|
1080
|
+
const debug = mergedConfig.debug ?? false;
|
|
1081
|
+
const logger = mergedConfig.logger ?? consola;
|
|
1082
|
+
if (debug)
|
|
1083
|
+
logger.level = LogLevels.debug;
|
|
1084
|
+
const browser = mergedConfig.browser ?? "chrome";
|
|
1085
|
+
const manifestVersion = mergedConfig.manifestVersion ?? (browser === "firefox" || browser === "safari" ? 2 : 3);
|
|
1086
|
+
const mode = mergedConfig.mode ?? (command === "build" ? "production" : "development");
|
|
1087
|
+
const env = { browser, command, manifestVersion, mode };
|
|
1088
|
+
const root = path5.resolve(
|
|
1089
|
+
inlineConfig.root ?? userConfig.root ?? process.cwd()
|
|
1090
|
+
);
|
|
1091
|
+
const wxtDir = path5.resolve(root, ".wxt");
|
|
1092
|
+
const srcDir = path5.resolve(root, mergedConfig.srcDir ?? root);
|
|
1093
|
+
const entrypointsDir = path5.resolve(
|
|
1094
|
+
srcDir,
|
|
1095
|
+
mergedConfig.entrypointsDir ?? "entrypoints"
|
|
1096
|
+
);
|
|
1097
|
+
const publicDir = path5.resolve(srcDir, mergedConfig.publicDir ?? "public");
|
|
1098
|
+
const typesDir = path5.resolve(wxtDir, "types");
|
|
1099
|
+
const outBaseDir = path5.resolve(root, ".output");
|
|
1100
|
+
const outDir = path5.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
|
|
1101
|
+
const runnerConfig = await loadConfig({
|
|
1102
|
+
name: "web-ext",
|
|
1103
|
+
cwd: root,
|
|
1104
|
+
globalRc: true,
|
|
1105
|
+
rcFile: ".webextrc",
|
|
1106
|
+
overrides: inlineConfig.runner,
|
|
1107
|
+
defaults: userConfig.runner
|
|
1108
|
+
});
|
|
1109
|
+
const finalConfig = {
|
|
1110
|
+
browser,
|
|
1111
|
+
command,
|
|
1112
|
+
debug,
|
|
1113
|
+
entrypointsDir,
|
|
1114
|
+
env,
|
|
1115
|
+
fsCache: createFsCache(wxtDir),
|
|
1116
|
+
imports: mergedConfig.imports ?? {},
|
|
1117
|
+
logger,
|
|
1118
|
+
manifest: await resolveManifestConfig(env, mergedConfig.manifest),
|
|
1119
|
+
manifestVersion,
|
|
1120
|
+
mode,
|
|
1121
|
+
outBaseDir,
|
|
1122
|
+
outDir,
|
|
1123
|
+
publicDir,
|
|
1124
|
+
root,
|
|
1125
|
+
runnerConfig,
|
|
1126
|
+
srcDir,
|
|
1127
|
+
typesDir,
|
|
1128
|
+
vite: () => ({}),
|
|
1129
|
+
// Real value added after this object is initialized.
|
|
1130
|
+
wxtDir,
|
|
1131
|
+
zip: resolveInternalZipConfig(root, mergedConfig),
|
|
1132
|
+
transformManifest(manifest) {
|
|
1133
|
+
userConfig.transformManifest?.(manifest);
|
|
1134
|
+
inlineConfig.transformManifest?.(manifest);
|
|
1135
|
+
},
|
|
1136
|
+
analysis: {
|
|
1137
|
+
enabled: mergedConfig.analysis?.enabled ?? false,
|
|
1138
|
+
template: mergedConfig.analysis?.template ?? "treemap"
|
|
1139
|
+
},
|
|
1140
|
+
userConfigMetadata: userConfigMetadata ?? {}
|
|
1141
|
+
};
|
|
1142
|
+
finalConfig.vite = (env2) => resolveInternalViteConfig(env2, mergedConfig, finalConfig);
|
|
1143
|
+
return finalConfig;
|
|
1144
|
+
}
|
|
1145
|
+
async function resolveManifestConfig(env, manifest) {
|
|
1146
|
+
return await (typeof manifest === "function" ? manifest(env) : manifest ?? {});
|
|
1147
|
+
}
|
|
1148
|
+
function mergeInlineConfig(inlineConfig, userConfig) {
|
|
1149
|
+
let imports;
|
|
1150
|
+
if (inlineConfig.imports === false || userConfig.imports === false) {
|
|
1151
|
+
imports = false;
|
|
1152
|
+
} else if (userConfig.imports == null && inlineConfig.imports == null) {
|
|
1153
|
+
imports = void 0;
|
|
1154
|
+
} else {
|
|
1155
|
+
imports = vite3.mergeConfig(
|
|
1156
|
+
userConfig.imports ?? {},
|
|
1157
|
+
inlineConfig.imports ?? {}
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
const manifest = async (env) => {
|
|
1161
|
+
const user = await resolveManifestConfig(env, userConfig.manifest);
|
|
1162
|
+
const inline = await resolveManifestConfig(env, inlineConfig.manifest);
|
|
1163
|
+
return vite3.mergeConfig(user, inline);
|
|
1164
|
+
};
|
|
1165
|
+
const viteConfig = async (env) => {
|
|
1166
|
+
const user = await userConfig.vite?.(env);
|
|
1167
|
+
const inline = await inlineConfig.vite?.(env);
|
|
1168
|
+
return vite3.mergeConfig(user ?? {}, inline ?? {});
|
|
1169
|
+
};
|
|
1170
|
+
const runner = vite3.mergeConfig(
|
|
1171
|
+
userConfig.runner ?? {},
|
|
1172
|
+
inlineConfig.runner ?? {}
|
|
1173
|
+
);
|
|
1174
|
+
const zip = vite3.mergeConfig(
|
|
1175
|
+
userConfig.zip ?? {},
|
|
1176
|
+
inlineConfig.zip ?? {}
|
|
1177
|
+
);
|
|
1178
|
+
return {
|
|
1179
|
+
root: inlineConfig.root ?? userConfig.root,
|
|
1180
|
+
browser: inlineConfig.browser ?? userConfig.browser,
|
|
1181
|
+
manifestVersion: inlineConfig.manifestVersion ?? userConfig.manifestVersion,
|
|
1182
|
+
configFile: inlineConfig.configFile,
|
|
1183
|
+
debug: inlineConfig.debug ?? userConfig.debug,
|
|
1184
|
+
entrypointsDir: inlineConfig.entrypointsDir ?? userConfig.entrypointsDir,
|
|
1185
|
+
imports,
|
|
1186
|
+
logger: inlineConfig.logger ?? userConfig.logger,
|
|
1187
|
+
manifest,
|
|
1188
|
+
mode: inlineConfig.mode ?? userConfig.mode,
|
|
1189
|
+
publicDir: inlineConfig.publicDir ?? userConfig.publicDir,
|
|
1190
|
+
runner,
|
|
1191
|
+
srcDir: inlineConfig.srcDir ?? userConfig.srcDir,
|
|
1192
|
+
vite: viteConfig,
|
|
1193
|
+
zip,
|
|
1194
|
+
analysis: {
|
|
1195
|
+
enabled: inlineConfig.analysis?.enabled ?? userConfig.analysis?.enabled,
|
|
1196
|
+
template: inlineConfig.analysis?.template ?? userConfig.analysis?.template
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
function resolveInternalZipConfig(root, mergedConfig) {
|
|
1201
|
+
return {
|
|
1202
|
+
sourcesTemplate: "{{name}}-{{version}}-sources.zip",
|
|
1203
|
+
artifactTemplate: "{{name}}-{{version}}-{{browser}}.zip",
|
|
1204
|
+
sourcesRoot: root,
|
|
1205
|
+
...mergedConfig.zip,
|
|
1206
|
+
ignoredSources: [
|
|
1207
|
+
"**/node_modules",
|
|
1208
|
+
// WXT files
|
|
1209
|
+
"**/web-ext.config.ts",
|
|
1210
|
+
// Hidden files
|
|
1211
|
+
"**/.*",
|
|
1212
|
+
// Tests
|
|
1213
|
+
"**/__tests__/**",
|
|
1214
|
+
"**/*.+(test|spec).?(c|m)+(j|t)s?(x)",
|
|
1215
|
+
// From user
|
|
1216
|
+
...mergedConfig.zip?.ignoredSources ?? []
|
|
1217
|
+
]
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
async function resolveInternalViteConfig(env, mergedConfig, finalConfig) {
|
|
1221
|
+
const internalVite = await mergedConfig.vite?.(env) ?? {};
|
|
1222
|
+
internalVite.root = finalConfig.root;
|
|
1223
|
+
internalVite.configFile = false;
|
|
1224
|
+
internalVite.logLevel = "warn";
|
|
1225
|
+
internalVite.mode = env.mode;
|
|
1226
|
+
internalVite.build ??= {};
|
|
1227
|
+
internalVite.build.outDir = finalConfig.outDir;
|
|
1228
|
+
internalVite.build.emptyOutDir = false;
|
|
1229
|
+
internalVite.plugins ??= [];
|
|
1230
|
+
internalVite.plugins.push(download(finalConfig));
|
|
1231
|
+
internalVite.plugins.push(devHtmlPrerender(finalConfig));
|
|
1232
|
+
internalVite.plugins.push(unimport(finalConfig));
|
|
1233
|
+
internalVite.plugins.push(
|
|
1234
|
+
virtualEntrypoint("background", finalConfig)
|
|
1235
|
+
);
|
|
1236
|
+
internalVite.plugins.push(
|
|
1237
|
+
virtualEntrypoint("content-script", finalConfig)
|
|
1238
|
+
);
|
|
1239
|
+
internalVite.plugins.push(
|
|
1240
|
+
virtualEntrypoint("unlisted-script", finalConfig)
|
|
1241
|
+
);
|
|
1242
|
+
internalVite.plugins.push(devServerGlobals(finalConfig));
|
|
1243
|
+
internalVite.plugins.push(tsconfigPaths(finalConfig));
|
|
1244
|
+
internalVite.plugins.push(noopBackground());
|
|
1245
|
+
if (finalConfig.analysis.enabled) {
|
|
1246
|
+
internalVite.plugins.push(bundleAnalysis());
|
|
1247
|
+
}
|
|
1248
|
+
internalVite.plugins.push(globals(finalConfig));
|
|
1249
|
+
return internalVite;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// src/core/utils/building/group-entrypoints.ts
|
|
1253
|
+
function groupEntrypoints(entrypoints) {
|
|
1254
|
+
const groupIndexMap = {};
|
|
1255
|
+
const groups = [];
|
|
1256
|
+
for (const entry of entrypoints) {
|
|
1257
|
+
const group = ENTRY_TYPE_TO_GROUP_MAP[entry.type];
|
|
1258
|
+
if (group === "no-group") {
|
|
1259
|
+
groups.push(entry);
|
|
1260
|
+
} else {
|
|
1261
|
+
let groupIndex = groupIndexMap[group];
|
|
1262
|
+
if (groupIndex == null) {
|
|
1263
|
+
groupIndex = groups.push([]) - 1;
|
|
1264
|
+
groupIndexMap[group] = groupIndex;
|
|
1265
|
+
}
|
|
1266
|
+
groups[groupIndex].push(entry);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return groups;
|
|
1270
|
+
}
|
|
1271
|
+
var ENTRY_TYPE_TO_GROUP_MAP = {
|
|
1272
|
+
sandbox: "sandbox-page",
|
|
1273
|
+
popup: "extension-page",
|
|
1274
|
+
newtab: "extension-page",
|
|
1275
|
+
history: "extension-page",
|
|
1276
|
+
options: "extension-page",
|
|
1277
|
+
devtools: "extension-page",
|
|
1278
|
+
bookmarks: "extension-page",
|
|
1279
|
+
sidepanel: "extension-page",
|
|
1280
|
+
"unlisted-page": "extension-page",
|
|
1281
|
+
background: "no-group",
|
|
1282
|
+
"content-script": "no-group",
|
|
1283
|
+
"unlisted-script": "no-group",
|
|
1284
|
+
"unlisted-style": "no-group",
|
|
1285
|
+
"content-script-style": "no-group"
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// src/core/utils/building/import-entrypoint.ts
|
|
1289
|
+
import createJITI from "jiti";
|
|
1290
|
+
import { createUnimport as createUnimport3 } from "unimport";
|
|
1291
|
+
import fs7 from "fs-extra";
|
|
1292
|
+
import { resolve as resolve8 } from "path";
|
|
1293
|
+
|
|
1294
|
+
// src/core/utils/strings.ts
|
|
1295
|
+
function kebabCaseAlphanumeric(str) {
|
|
1296
|
+
return str.toLowerCase().replace(/[^a-z0-9-\s]/g, "").replace(/\s+/g, "-");
|
|
1297
|
+
}
|
|
1298
|
+
function removeImportStatements(text) {
|
|
1299
|
+
return text.replace(
|
|
1300
|
+
/(import\s?[{\w][\s\S]*?from\s?["'][\s\S]*?["'];?|import\s?["'][\s\S]*?["'];?)/gm,
|
|
1301
|
+
""
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
function removeProjectImportStatements(text) {
|
|
1305
|
+
const noImports = removeImportStatements(text);
|
|
1306
|
+
return `import { defineContentScript, defineBackground } from 'wxt/client';
|
|
1307
|
+
import { defineUnlistedScript } from 'wxt/sandbox';
|
|
1308
|
+
|
|
1309
|
+
${noImports}`;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// src/core/utils/building/import-entrypoint.ts
|
|
1313
|
+
import { transformSync } from "esbuild";
|
|
1314
|
+
async function importEntrypointFile(path7, config) {
|
|
1315
|
+
config.logger.debug("Loading file metadata:", path7);
|
|
1316
|
+
const normalPath = normalizePath2(path7);
|
|
1317
|
+
const unimport2 = createUnimport3({
|
|
1318
|
+
...getUnimportOptions(config),
|
|
1319
|
+
// Only allow specific imports, not all from the project
|
|
1320
|
+
dirs: []
|
|
1321
|
+
});
|
|
1322
|
+
await unimport2.init();
|
|
1323
|
+
const text = await fs7.readFile(path7, "utf-8");
|
|
1324
|
+
const textNoImports = removeProjectImportStatements(text);
|
|
1325
|
+
const { code } = await unimport2.injectImports(textNoImports);
|
|
1326
|
+
config.logger.debug(
|
|
1327
|
+
["Text:", text, "No imports:", textNoImports, "Code:", code].join("\n")
|
|
1328
|
+
);
|
|
1329
|
+
const jiti = createJITI(__filename, {
|
|
1330
|
+
cache: false,
|
|
1331
|
+
debug: config.debug,
|
|
1332
|
+
esmResolve: true,
|
|
1333
|
+
alias: {
|
|
1334
|
+
"webextension-polyfill": resolve8(
|
|
1335
|
+
config.root,
|
|
1336
|
+
"node_modules/wxt/dist/virtual/mock-browser.js"
|
|
1337
|
+
)
|
|
1338
|
+
},
|
|
1339
|
+
// List of extensions to transform with esbuild
|
|
1340
|
+
extensions: [".ts", ".cts", ".mts", ".tsx", ".js", ".cjs", ".mjs", ".jsx"],
|
|
1341
|
+
transform(opts) {
|
|
1342
|
+
const isEntrypoint = opts.filename === normalPath;
|
|
1343
|
+
return transformSync(
|
|
1344
|
+
// Use modified source code for entrypoints
|
|
1345
|
+
isEntrypoint ? code : opts.source,
|
|
1346
|
+
getEsbuildOptions(opts)
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
try {
|
|
1351
|
+
const res = await jiti(path7);
|
|
1352
|
+
return res.default;
|
|
1353
|
+
} catch (err) {
|
|
1354
|
+
config.logger.error(err);
|
|
1355
|
+
throw err;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
function getEsbuildOptions(opts) {
|
|
1359
|
+
const isJsx = opts.filename?.endsWith("x");
|
|
1360
|
+
return {
|
|
1361
|
+
format: "cjs",
|
|
1362
|
+
loader: isJsx ? "tsx" : "ts",
|
|
1363
|
+
jsx: isJsx ? "automatic" : void 0
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// src/core/utils/building/internal-build.ts
|
|
1368
|
+
import pc4 from "picocolors";
|
|
1369
|
+
import * as vite5 from "vite";
|
|
1370
|
+
import fs11 from "fs-extra";
|
|
1371
|
+
|
|
1372
|
+
// src/core/utils/log/printBuildSummary.ts
|
|
1373
|
+
import { resolve as resolve9 } from "path";
|
|
1374
|
+
|
|
1375
|
+
// src/core/utils/log/printFileList.ts
|
|
1376
|
+
import path6 from "node:path";
|
|
1377
|
+
import pc2 from "picocolors";
|
|
1378
|
+
import fs8 from "fs-extra";
|
|
1379
|
+
import { filesize } from "filesize";
|
|
1380
|
+
|
|
1381
|
+
// src/core/utils/log/printTable.ts
|
|
1382
|
+
function printTable(log, header, rows, gap = 2) {
|
|
1383
|
+
if (rows.length === 0)
|
|
1384
|
+
return;
|
|
1385
|
+
const columnWidths = rows.reduce(
|
|
1386
|
+
(widths, row) => {
|
|
1387
|
+
for (let i = 0; i < Math.max(widths.length, row.length); i++) {
|
|
1388
|
+
widths[i] = Math.max(row[i]?.length ?? 0, widths[i] ?? 0);
|
|
1389
|
+
}
|
|
1390
|
+
return widths;
|
|
1391
|
+
},
|
|
1392
|
+
rows[0].map((column) => column.length)
|
|
1393
|
+
);
|
|
1394
|
+
let str = "";
|
|
1395
|
+
rows.forEach((row, i) => {
|
|
1396
|
+
row.forEach((col, j) => {
|
|
1397
|
+
str += col.padEnd(columnWidths[j], " ");
|
|
1398
|
+
if (j !== row.length - 1)
|
|
1399
|
+
str += "".padEnd(gap, " ");
|
|
1400
|
+
});
|
|
1401
|
+
if (i !== rows.length - 1)
|
|
1402
|
+
str += "\n";
|
|
1403
|
+
});
|
|
1404
|
+
log(`${header}
|
|
1405
|
+
${str}`);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// src/core/utils/log/printFileList.ts
|
|
1409
|
+
async function printFileList(log, header, baseDir, files) {
|
|
1410
|
+
let totalSize = 0;
|
|
1411
|
+
const fileRows = await Promise.all(
|
|
1412
|
+
files.map(async (file, i) => {
|
|
1413
|
+
const parts = [
|
|
1414
|
+
path6.relative(process.cwd(), baseDir) + path6.sep,
|
|
1415
|
+
path6.relative(baseDir, file)
|
|
1416
|
+
];
|
|
1417
|
+
const prefix = i === files.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
|
|
1418
|
+
const color = getChunkColor(file);
|
|
1419
|
+
const stats = await fs8.lstat(file);
|
|
1420
|
+
totalSize += stats.size;
|
|
1421
|
+
const size = String(filesize(stats.size));
|
|
1422
|
+
return [
|
|
1423
|
+
`${pc2.gray(prefix)} ${pc2.dim(parts[0])}${color(parts[1])}`,
|
|
1424
|
+
pc2.dim(size)
|
|
1425
|
+
];
|
|
1426
|
+
})
|
|
1427
|
+
);
|
|
1428
|
+
fileRows.push([`${pc2.cyan("\u03A3 Total size:")} ${String(filesize(totalSize))}`]);
|
|
1429
|
+
printTable(log, header, fileRows);
|
|
1430
|
+
}
|
|
1431
|
+
var DEFAULT_COLOR = pc2.blue;
|
|
1432
|
+
var CHUNK_COLORS = {
|
|
1433
|
+
".js.map": pc2.gray,
|
|
1434
|
+
".cjs.map": pc2.gray,
|
|
1435
|
+
".mjs.map": pc2.gray,
|
|
1436
|
+
".html": pc2.green,
|
|
1437
|
+
".css": pc2.magenta,
|
|
1438
|
+
".js": pc2.cyan,
|
|
1439
|
+
".cjs": pc2.cyan,
|
|
1440
|
+
".mjs": pc2.cyan,
|
|
1441
|
+
".zip": pc2.yellow
|
|
1442
|
+
};
|
|
1443
|
+
function getChunkColor(filename) {
|
|
1444
|
+
return Object.entries(CHUNK_COLORS).find(([key]) => filename.endsWith(key))?.[1] ?? DEFAULT_COLOR;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// src/core/utils/log/printBuildSummary.ts
|
|
1448
|
+
async function printBuildSummary(log, header, output, config) {
|
|
1449
|
+
const chunks = [
|
|
1450
|
+
...output.steps.flatMap((step) => step.chunks),
|
|
1451
|
+
...output.publicAssets
|
|
1452
|
+
].sort((l, r) => {
|
|
1453
|
+
const lWeight = getChunkSortWeight(l.fileName);
|
|
1454
|
+
const rWeight = getChunkSortWeight(r.fileName);
|
|
1455
|
+
const diff = lWeight - rWeight;
|
|
1456
|
+
if (diff !== 0)
|
|
1457
|
+
return diff;
|
|
1458
|
+
return l.fileName.localeCompare(r.fileName);
|
|
1459
|
+
});
|
|
1460
|
+
const files = chunks.map((chunk) => resolve9(config.outDir, chunk.fileName));
|
|
1461
|
+
await printFileList(log, header, config.outDir, files);
|
|
1462
|
+
}
|
|
1463
|
+
var DEFAULT_SORT_WEIGHT = 100;
|
|
1464
|
+
var CHUNK_SORT_WEIGHTS = {
|
|
1465
|
+
"manifest.json": 0,
|
|
1466
|
+
".html": 1,
|
|
1467
|
+
".js.map": 2,
|
|
1468
|
+
".js": 2,
|
|
1469
|
+
".css": 3
|
|
1470
|
+
};
|
|
1471
|
+
function getChunkSortWeight(filename) {
|
|
1472
|
+
return Object.entries(CHUNK_SORT_WEIGHTS).find(
|
|
1473
|
+
([key]) => filename.endsWith(key)
|
|
1474
|
+
)?.[1] ?? DEFAULT_SORT_WEIGHT;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/core/utils/log/printHeader.ts
|
|
1478
|
+
import pc3 from "picocolors";
|
|
1479
|
+
import { consola as consola2 } from "consola";
|
|
1480
|
+
|
|
1481
|
+
// src/core/utils/building/internal-build.ts
|
|
1482
|
+
import glob2 from "fast-glob";
|
|
1483
|
+
|
|
1484
|
+
// src/core/utils/manifest.ts
|
|
1485
|
+
import fs10 from "fs-extra";
|
|
1486
|
+
import { resolve as resolve11 } from "path";
|
|
1487
|
+
|
|
1488
|
+
// src/core/utils/content-security-policy.ts
|
|
1489
|
+
var ContentSecurityPolicy = class _ContentSecurityPolicy {
|
|
1490
|
+
static DIRECTIVE_ORDER = {
|
|
1491
|
+
"default-src": 0,
|
|
1492
|
+
"script-src": 1,
|
|
1493
|
+
"object-src": 2
|
|
1494
|
+
};
|
|
1495
|
+
data;
|
|
1496
|
+
constructor(csp) {
|
|
1497
|
+
if (csp) {
|
|
1498
|
+
const sections = csp.split(";").map((section) => section.trim());
|
|
1499
|
+
this.data = sections.reduce((data, section) => {
|
|
1500
|
+
const [key, ...values] = section.split(" ").map((item) => item.trim());
|
|
1501
|
+
if (key)
|
|
1502
|
+
data[key] = values;
|
|
1503
|
+
return data;
|
|
1504
|
+
}, {});
|
|
1505
|
+
} else {
|
|
1506
|
+
this.data = {};
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Ensure a set of values are listed under a directive.
|
|
1511
|
+
*/
|
|
1512
|
+
add(directive, ...newValues) {
|
|
1513
|
+
const values = this.data[directive] ?? [];
|
|
1514
|
+
newValues.forEach((newValue) => {
|
|
1515
|
+
if (!values.includes(newValue))
|
|
1516
|
+
values.push(newValue);
|
|
1517
|
+
});
|
|
1518
|
+
this.data[directive] = values;
|
|
1519
|
+
return this;
|
|
1520
|
+
}
|
|
1521
|
+
toString() {
|
|
1522
|
+
const directives = Object.entries(this.data).sort(([l], [r]) => {
|
|
1523
|
+
const lo = _ContentSecurityPolicy.DIRECTIVE_ORDER[l] ?? 2;
|
|
1524
|
+
const ro = _ContentSecurityPolicy.DIRECTIVE_ORDER[r] ?? 2;
|
|
1525
|
+
return lo - ro;
|
|
1526
|
+
});
|
|
1527
|
+
return directives.map((entry) => entry.flat().join(" ")).join("; ") + ";";
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
|
|
1531
|
+
// src/core/utils/content-scripts.ts
|
|
1532
|
+
function hashContentScriptOptions(options, config) {
|
|
1533
|
+
const simplifiedOptions = mapWxtOptionsToContentScript(options, config);
|
|
1534
|
+
Object.keys(simplifiedOptions).forEach((key) => {
|
|
1535
|
+
if (simplifiedOptions[key] == null)
|
|
1536
|
+
delete simplifiedOptions[key];
|
|
1537
|
+
});
|
|
1538
|
+
const withDefaults = {
|
|
1539
|
+
exclude_globs: [],
|
|
1540
|
+
exclude_matches: [],
|
|
1541
|
+
include_globs: [],
|
|
1542
|
+
match_about_blank: false,
|
|
1543
|
+
run_at: "document_idle",
|
|
1544
|
+
all_frames: false,
|
|
1545
|
+
// @ts-expect-error - not in type
|
|
1546
|
+
match_origin_as_fallback: false,
|
|
1547
|
+
world: "ISOLATED",
|
|
1548
|
+
...simplifiedOptions
|
|
1549
|
+
};
|
|
1550
|
+
return JSON.stringify(
|
|
1551
|
+
Object.entries(withDefaults).map(([key, value]) => {
|
|
1552
|
+
if (Array.isArray(value))
|
|
1553
|
+
return [key, value.sort()];
|
|
1554
|
+
else
|
|
1555
|
+
return [key, value];
|
|
1556
|
+
}).sort((l, r) => l[0].localeCompare(r[0]))
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
function mapWxtOptionsToContentScript(options, config) {
|
|
1560
|
+
return {
|
|
1561
|
+
matches: resolvePerBrowserOption(options.matches, config.browser),
|
|
1562
|
+
all_frames: resolvePerBrowserOption(options.allFrames, config.browser),
|
|
1563
|
+
match_about_blank: resolvePerBrowserOption(
|
|
1564
|
+
options.matchAboutBlank,
|
|
1565
|
+
config.browser
|
|
1566
|
+
),
|
|
1567
|
+
exclude_globs: resolvePerBrowserOption(
|
|
1568
|
+
options.excludeGlobs,
|
|
1569
|
+
config.browser
|
|
1570
|
+
),
|
|
1571
|
+
exclude_matches: resolvePerBrowserOption(
|
|
1572
|
+
options.excludeMatches,
|
|
1573
|
+
config.browser
|
|
1574
|
+
),
|
|
1575
|
+
include_globs: resolvePerBrowserOption(
|
|
1576
|
+
options.includeGlobs,
|
|
1577
|
+
config.browser
|
|
1578
|
+
),
|
|
1579
|
+
run_at: resolvePerBrowserOption(options.runAt, config.browser),
|
|
1580
|
+
// @ts-expect-error: untyped chrome options
|
|
1581
|
+
match_origin_as_fallback: resolvePerBrowserOption(
|
|
1582
|
+
options.matchOriginAsFallback,
|
|
1583
|
+
config.browser
|
|
1584
|
+
),
|
|
1585
|
+
world: options.world
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/core/utils/package.ts
|
|
1590
|
+
import { resolve as resolve10 } from "node:path";
|
|
1591
|
+
import fs9 from "fs-extra";
|
|
1592
|
+
async function getPackageJson(config) {
|
|
1593
|
+
const file = resolve10(config.root, "package.json");
|
|
1594
|
+
try {
|
|
1595
|
+
return await fs9.readJson(file);
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
config.logger.debug(
|
|
1598
|
+
`Failed to read package.json at: ${file}. Returning undefined.`
|
|
1599
|
+
);
|
|
1600
|
+
return {};
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// src/core/utils/manifest.ts
|
|
1605
|
+
import { produce } from "immer";
|
|
1606
|
+
import * as vite4 from "vite";
|
|
1607
|
+
async function writeManifest(manifest, output, config) {
|
|
1608
|
+
const str = config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
|
|
1609
|
+
await fs10.ensureDir(config.outDir);
|
|
1610
|
+
await writeFileIfDifferent(resolve11(config.outDir, "manifest.json"), str);
|
|
1611
|
+
output.publicAssets.unshift({
|
|
1612
|
+
type: "asset",
|
|
1613
|
+
fileName: "manifest.json",
|
|
1614
|
+
name: "manifest",
|
|
1615
|
+
needsCodeReference: false,
|
|
1616
|
+
source: str
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
async function generateMainfest(entrypoints, buildOutput, config) {
|
|
1620
|
+
const pkg = await getPackageJson(config);
|
|
1621
|
+
const versionName = config.manifest.version_name ?? pkg?.version;
|
|
1622
|
+
const version3 = config.manifest.version ?? simplifyVersion(pkg?.version);
|
|
1623
|
+
const baseManifest = {
|
|
1624
|
+
manifest_version: config.manifestVersion,
|
|
1625
|
+
name: pkg?.name,
|
|
1626
|
+
description: pkg?.description,
|
|
1627
|
+
version: version3,
|
|
1628
|
+
version_name: (
|
|
1629
|
+
// Firefox doesn't support version_name
|
|
1630
|
+
config.browser === "firefox" || versionName === version3 ? void 0 : versionName
|
|
1631
|
+
),
|
|
1632
|
+
short_name: pkg?.shortName,
|
|
1633
|
+
icons: discoverIcons(buildOutput)
|
|
1634
|
+
};
|
|
1635
|
+
const userManifest = config.manifest;
|
|
1636
|
+
const manifest = vite4.mergeConfig(
|
|
1637
|
+
baseManifest,
|
|
1638
|
+
userManifest
|
|
1639
|
+
);
|
|
1640
|
+
addEntrypoints(manifest, entrypoints, buildOutput, config);
|
|
1641
|
+
if (config.command === "serve")
|
|
1642
|
+
addDevModeCsp(manifest, config);
|
|
1643
|
+
if (config.command === "serve")
|
|
1644
|
+
addDevModePermissions(manifest, config);
|
|
1645
|
+
const finalManifest = produce(manifest, config.transformManifest);
|
|
1646
|
+
if (finalManifest.name == null)
|
|
1647
|
+
throw Error(
|
|
1648
|
+
"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"
|
|
1649
|
+
);
|
|
1650
|
+
if (finalManifest.version == null) {
|
|
1651
|
+
throw Error(
|
|
1652
|
+
"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"
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
return finalManifest;
|
|
1656
|
+
}
|
|
1657
|
+
function simplifyVersion(versionName) {
|
|
1658
|
+
const version3 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
|
|
1659
|
+
versionName
|
|
1660
|
+
)?.[1];
|
|
1661
|
+
if (version3 == null)
|
|
1662
|
+
throw Error(
|
|
1663
|
+
`Cannot simplify package.json version "${versionName}" to a valid extension version, "X.Y.Z"`
|
|
1664
|
+
);
|
|
1665
|
+
return version3;
|
|
1666
|
+
}
|
|
1667
|
+
function addEntrypoints(manifest, entrypoints, buildOutput, config) {
|
|
1668
|
+
const entriesByType = entrypoints.reduce((map, entrypoint) => {
|
|
1669
|
+
map[entrypoint.type] ??= [];
|
|
1670
|
+
map[entrypoint.type]?.push(entrypoint);
|
|
1671
|
+
return map;
|
|
1672
|
+
}, {});
|
|
1673
|
+
const background = entriesByType["background"]?.[0];
|
|
1674
|
+
const bookmarks = entriesByType["bookmarks"]?.[0];
|
|
1675
|
+
const contentScripts = entriesByType["content-script"];
|
|
1676
|
+
const devtools = entriesByType["devtools"]?.[0];
|
|
1677
|
+
const history = entriesByType["history"]?.[0];
|
|
1678
|
+
const newtab = entriesByType["newtab"]?.[0];
|
|
1679
|
+
const options = entriesByType["options"]?.[0];
|
|
1680
|
+
const popup = entriesByType["popup"]?.[0];
|
|
1681
|
+
const sandboxes = entriesByType["sandbox"];
|
|
1682
|
+
const sidepanels = entriesByType["sidepanel"];
|
|
1683
|
+
if (background) {
|
|
1684
|
+
const script = getEntrypointBundlePath(background, config.outDir, ".js");
|
|
1685
|
+
if (manifest.manifest_version === 3) {
|
|
1686
|
+
manifest.background = {
|
|
1687
|
+
type: background.options.type,
|
|
1688
|
+
service_worker: script
|
|
1689
|
+
};
|
|
1690
|
+
} else {
|
|
1691
|
+
manifest.background = {
|
|
1692
|
+
persistent: background.options.persistent,
|
|
1693
|
+
scripts: [script]
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (bookmarks) {
|
|
1698
|
+
if (config.browser === "firefox") {
|
|
1699
|
+
config.logger.warn(
|
|
1700
|
+
"Bookmarks are not supported by Firefox. chrome_url_overrides.bookmarks was not added to the manifest"
|
|
1701
|
+
);
|
|
1702
|
+
} else {
|
|
1703
|
+
manifest.chrome_url_overrides ??= {};
|
|
1704
|
+
manifest.chrome_url_overrides.bookmarks = getEntrypointBundlePath(
|
|
1705
|
+
bookmarks,
|
|
1706
|
+
config.outDir,
|
|
1707
|
+
".html"
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
if (history) {
|
|
1712
|
+
if (config.browser === "firefox") {
|
|
1713
|
+
config.logger.warn(
|
|
1714
|
+
"Bookmarks are not supported by Firefox. chrome_url_overrides.history was not added to the manifest"
|
|
1715
|
+
);
|
|
1716
|
+
} else {
|
|
1717
|
+
manifest.chrome_url_overrides ??= {};
|
|
1718
|
+
manifest.chrome_url_overrides.history = getEntrypointBundlePath(
|
|
1719
|
+
history,
|
|
1720
|
+
config.outDir,
|
|
1721
|
+
".html"
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
if (newtab) {
|
|
1726
|
+
manifest.chrome_url_overrides ??= {};
|
|
1727
|
+
manifest.chrome_url_overrides.newtab = getEntrypointBundlePath(
|
|
1728
|
+
newtab,
|
|
1729
|
+
config.outDir,
|
|
1730
|
+
".html"
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
if (popup) {
|
|
1734
|
+
const default_popup = getEntrypointBundlePath(
|
|
1735
|
+
popup,
|
|
1736
|
+
config.outDir,
|
|
1737
|
+
".html"
|
|
1738
|
+
);
|
|
1739
|
+
const options2 = {};
|
|
1740
|
+
if (popup.options.defaultIcon)
|
|
1741
|
+
options2.default_icon = popup.options.defaultIcon;
|
|
1742
|
+
if (popup.options.defaultTitle)
|
|
1743
|
+
options2.default_title = popup.options.defaultTitle;
|
|
1744
|
+
if (popup.options.browserStyle)
|
|
1745
|
+
options2.browser_style = popup.options.browserStyle;
|
|
1746
|
+
if (manifest.manifest_version === 3) {
|
|
1747
|
+
manifest.action = {
|
|
1748
|
+
...manifest.action ?? {},
|
|
1749
|
+
...options2,
|
|
1750
|
+
default_popup
|
|
1751
|
+
};
|
|
1752
|
+
} else {
|
|
1753
|
+
const key = popup.options.mv2Key ?? "browser_action";
|
|
1754
|
+
manifest[key] = {
|
|
1755
|
+
...manifest[key] ?? {},
|
|
1756
|
+
...options2,
|
|
1757
|
+
default_popup
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
if (devtools) {
|
|
1762
|
+
manifest.devtools_page = getEntrypointBundlePath(
|
|
1763
|
+
devtools,
|
|
1764
|
+
config.outDir,
|
|
1765
|
+
".html"
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
if (options) {
|
|
1769
|
+
const page = getEntrypointBundlePath(options, config.outDir, ".html");
|
|
1770
|
+
manifest.options_ui = {
|
|
1771
|
+
open_in_tab: options.options.openInTab,
|
|
1772
|
+
browser_style: config.browser === "firefox" ? options.options.browserStyle : void 0,
|
|
1773
|
+
chrome_style: config.browser !== "firefox" ? options.options.chromeStyle : void 0,
|
|
1774
|
+
page
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
if (sandboxes?.length) {
|
|
1778
|
+
if (config.browser === "firefox") {
|
|
1779
|
+
config.logger.warn(
|
|
1780
|
+
"Sandboxed pages not supported by Firefox. sandbox.pages was not added to the manifest"
|
|
1781
|
+
);
|
|
1782
|
+
} else {
|
|
1783
|
+
manifest.sandbox = {
|
|
1784
|
+
pages: sandboxes.map(
|
|
1785
|
+
(entry) => getEntrypointBundlePath(entry, config.outDir, ".html")
|
|
1786
|
+
)
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
if (sidepanels?.length) {
|
|
1791
|
+
const defaultSidepanel = sidepanels.find((entry) => entry.name === "sidepanel") ?? sidepanels[0];
|
|
1792
|
+
const page = getEntrypointBundlePath(
|
|
1793
|
+
defaultSidepanel,
|
|
1794
|
+
config.outDir,
|
|
1795
|
+
".html"
|
|
1796
|
+
);
|
|
1797
|
+
if (config.browser === "firefox") {
|
|
1798
|
+
manifest.sidebar_action = {
|
|
1799
|
+
// TODO: Add options to side panel
|
|
1800
|
+
// ...defaultSidepanel.options,
|
|
1801
|
+
default_panel: page
|
|
1802
|
+
};
|
|
1803
|
+
} else if (config.manifestVersion === 3) {
|
|
1804
|
+
manifest.side_panel = {
|
|
1805
|
+
default_path: page
|
|
1806
|
+
};
|
|
1807
|
+
} else {
|
|
1808
|
+
config.logger.warn(
|
|
1809
|
+
"Side panel not supported by Chromium using MV2. side_panel.default_path was not added to the manifest"
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (contentScripts?.length) {
|
|
1814
|
+
const cssMap = getContentScriptsCssMap(buildOutput, contentScripts);
|
|
1815
|
+
if (config.command === "serve" && config.manifestVersion === 3) {
|
|
1816
|
+
const hostPermissions = new Set(manifest.host_permissions ?? []);
|
|
1817
|
+
contentScripts.forEach((script) => {
|
|
1818
|
+
const matches = resolvePerBrowserOption(
|
|
1819
|
+
script.options.matches,
|
|
1820
|
+
config.browser
|
|
1821
|
+
);
|
|
1822
|
+
matches.forEach((matchPattern) => {
|
|
1823
|
+
hostPermissions.add(matchPattern);
|
|
1824
|
+
});
|
|
1825
|
+
});
|
|
1826
|
+
hostPermissions.forEach(
|
|
1827
|
+
(permission) => addHostPermission(manifest, permission)
|
|
1828
|
+
);
|
|
1829
|
+
} else {
|
|
1830
|
+
const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
|
|
1831
|
+
const hash = hashContentScriptOptions(script.options, config);
|
|
1832
|
+
if (map.has(hash))
|
|
1833
|
+
map.get(hash)?.push(script);
|
|
1834
|
+
else
|
|
1835
|
+
map.set(hash, [script]);
|
|
1836
|
+
return map;
|
|
1837
|
+
}, /* @__PURE__ */ new Map());
|
|
1838
|
+
const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
|
|
1839
|
+
([, scripts]) => ({
|
|
1840
|
+
...mapWxtOptionsToContentScript(scripts[0].options, config),
|
|
1841
|
+
css: getContentScriptCssFiles(scripts, cssMap),
|
|
1842
|
+
js: scripts.map(
|
|
1843
|
+
(entry) => getEntrypointBundlePath(entry, config.outDir, ".js")
|
|
1844
|
+
)
|
|
1845
|
+
})
|
|
1846
|
+
);
|
|
1847
|
+
if (newContentScripts.length >= 0) {
|
|
1848
|
+
manifest.content_scripts ??= [];
|
|
1849
|
+
manifest.content_scripts.push(...newContentScripts);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
const contentScriptCssResources = getContentScriptCssWebAccessibleResources(
|
|
1853
|
+
config,
|
|
1854
|
+
contentScripts,
|
|
1855
|
+
cssMap
|
|
1856
|
+
);
|
|
1857
|
+
if (contentScriptCssResources.length > 0) {
|
|
1858
|
+
manifest.web_accessible_resources ??= [];
|
|
1859
|
+
manifest.web_accessible_resources.push(...contentScriptCssResources);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
function discoverIcons(buildOutput) {
|
|
1864
|
+
const icons = [];
|
|
1865
|
+
const iconRegex = [
|
|
1866
|
+
/^icon-([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/,
|
|
1867
|
+
// icon-16.png
|
|
1868
|
+
/^icon-([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/,
|
|
1869
|
+
// icon-16x16.png
|
|
1870
|
+
/^icon@([0-9]+)w\.(png|bmp|jpeg|jpg|ico|gif)$/,
|
|
1871
|
+
// icon@16w.png
|
|
1872
|
+
/^icon@([0-9]+)h\.(png|bmp|jpeg|jpg|ico|gif)$/,
|
|
1873
|
+
// icon@16h.png
|
|
1874
|
+
/^icon@([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/,
|
|
1875
|
+
// icon@16.png
|
|
1876
|
+
/^icon[\/\\]([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/,
|
|
1877
|
+
// icon/16.png
|
|
1878
|
+
/^icon[\/\\]([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/
|
|
1879
|
+
// icon/16x16.png
|
|
1880
|
+
];
|
|
1881
|
+
buildOutput.publicAssets.forEach((asset) => {
|
|
1882
|
+
let size;
|
|
1883
|
+
for (const regex of iconRegex) {
|
|
1884
|
+
const match = asset.fileName.match(regex);
|
|
1885
|
+
if (match?.[1] != null) {
|
|
1886
|
+
size = match[1];
|
|
1887
|
+
break;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
if (size == null)
|
|
1891
|
+
return;
|
|
1892
|
+
icons.push([size, normalizePath2(asset.fileName)]);
|
|
1893
|
+
});
|
|
1894
|
+
return icons.length > 0 ? Object.fromEntries(icons) : void 0;
|
|
1895
|
+
}
|
|
1896
|
+
function addDevModeCsp(manifest, config) {
|
|
1897
|
+
const permission = `http://${config.server?.hostname ?? ""}/*`;
|
|
1898
|
+
const allowedCsp = config.server?.origin ?? "http://localhost:*";
|
|
1899
|
+
if (manifest.manifest_version === 3) {
|
|
1900
|
+
addHostPermission(manifest, permission);
|
|
1901
|
+
} else {
|
|
1902
|
+
addPermission(manifest, permission);
|
|
1903
|
+
}
|
|
1904
|
+
const csp = new ContentSecurityPolicy(
|
|
1905
|
+
manifest.manifest_version === 3 ? (
|
|
1906
|
+
// @ts-expect-error: extension_pages is not typed
|
|
1907
|
+
manifest.content_security_policy?.extension_pages ?? "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
|
|
1908
|
+
) : manifest.content_security_policy ?? "script-src 'self'; object-src 'self';"
|
|
1909
|
+
// default CSP for MV2
|
|
1910
|
+
);
|
|
1911
|
+
if (config.server)
|
|
1912
|
+
csp.add("script-src", allowedCsp);
|
|
1913
|
+
if (manifest.manifest_version === 3) {
|
|
1914
|
+
manifest.content_security_policy ??= {};
|
|
1915
|
+
manifest.content_security_policy.extension_pages = csp.toString();
|
|
1916
|
+
} else {
|
|
1917
|
+
manifest.content_security_policy = csp.toString();
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
function addDevModePermissions(manifest, config) {
|
|
1921
|
+
addPermission(manifest, "tabs");
|
|
1922
|
+
if (config.manifestVersion === 3)
|
|
1923
|
+
addPermission(manifest, "scripting");
|
|
1924
|
+
}
|
|
1925
|
+
function getContentScriptCssFiles(contentScripts, contentScriptCssMap) {
|
|
1926
|
+
const css = [];
|
|
1927
|
+
contentScripts.forEach((script) => {
|
|
1928
|
+
if (script.options.cssInjectionMode === "manual" || script.options.cssInjectionMode === "ui")
|
|
1929
|
+
return;
|
|
1930
|
+
const cssFile = contentScriptCssMap[script.name];
|
|
1931
|
+
if (cssFile == null)
|
|
1932
|
+
return;
|
|
1933
|
+
if (cssFile)
|
|
1934
|
+
css.push(cssFile);
|
|
1935
|
+
});
|
|
1936
|
+
if (css.length > 0)
|
|
1937
|
+
return css;
|
|
1938
|
+
return void 0;
|
|
1939
|
+
}
|
|
1940
|
+
function getContentScriptCssWebAccessibleResources(config, contentScripts, contentScriptCssMap) {
|
|
1941
|
+
const resources = [];
|
|
1942
|
+
contentScripts.forEach((script) => {
|
|
1943
|
+
if (script.options.cssInjectionMode !== "ui")
|
|
1944
|
+
return;
|
|
1945
|
+
const cssFile = contentScriptCssMap[script.name];
|
|
1946
|
+
if (cssFile == null)
|
|
1947
|
+
return;
|
|
1948
|
+
if (config.manifestVersion === 2) {
|
|
1949
|
+
resources.push(cssFile);
|
|
1950
|
+
} else {
|
|
1951
|
+
resources.push({
|
|
1952
|
+
resources: [cssFile],
|
|
1953
|
+
matches: script.options.matches
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
return resources;
|
|
1958
|
+
}
|
|
1959
|
+
function getContentScriptsCssMap(buildOutput, scripts) {
|
|
1960
|
+
const map = {};
|
|
1961
|
+
const allChunks = buildOutput.steps.flatMap((step) => step.chunks);
|
|
1962
|
+
scripts.forEach((script) => {
|
|
1963
|
+
const relatedCss = allChunks.find(
|
|
1964
|
+
(chunk) => chunk.fileName === `content-scripts/${script.name}.css`
|
|
1965
|
+
);
|
|
1966
|
+
if (relatedCss != null)
|
|
1967
|
+
map[script.name] = relatedCss.fileName;
|
|
1968
|
+
});
|
|
1969
|
+
return map;
|
|
1970
|
+
}
|
|
1971
|
+
function addPermission(manifest, permission) {
|
|
1972
|
+
manifest.permissions ??= [];
|
|
1973
|
+
if (manifest.permissions.includes(permission))
|
|
1974
|
+
return;
|
|
1975
|
+
manifest.permissions.push(permission);
|
|
1976
|
+
}
|
|
1977
|
+
function addHostPermission(manifest, hostPermission) {
|
|
1978
|
+
manifest.host_permissions ??= [];
|
|
1979
|
+
if (manifest.host_permissions.includes(hostPermission))
|
|
1980
|
+
return;
|
|
1981
|
+
manifest.host_permissions.push(hostPermission);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// src/core/utils/building/rebuild.ts
|
|
1985
|
+
async function rebuild(config, entrypointGroups, existingOutput = {
|
|
1986
|
+
steps: [],
|
|
1987
|
+
publicAssets: []
|
|
1988
|
+
}) {
|
|
1989
|
+
const { default: ora } = await import("ora");
|
|
1990
|
+
const spinner = ora(`Preparing...`).start();
|
|
1991
|
+
const allEntrypoints = await findEntrypoints(config);
|
|
1992
|
+
await generateTypesDir(allEntrypoints, config).catch((err) => {
|
|
1993
|
+
config.logger.warn("Failed to update .wxt directory:", err);
|
|
1994
|
+
if (config.command === "build")
|
|
1995
|
+
throw err;
|
|
1996
|
+
});
|
|
1997
|
+
const newOutput = await buildEntrypoints(entrypointGroups, config, spinner);
|
|
1998
|
+
const mergedOutput = {
|
|
1999
|
+
steps: [...existingOutput.steps, ...newOutput.steps],
|
|
2000
|
+
publicAssets: [...existingOutput.publicAssets, ...newOutput.publicAssets]
|
|
2001
|
+
};
|
|
2002
|
+
const newManifest = await generateMainfest(
|
|
2003
|
+
allEntrypoints,
|
|
2004
|
+
mergedOutput,
|
|
2005
|
+
config
|
|
2006
|
+
);
|
|
2007
|
+
const finalOutput = {
|
|
2008
|
+
manifest: newManifest,
|
|
2009
|
+
...newOutput
|
|
2010
|
+
};
|
|
2011
|
+
await writeManifest(newManifest, finalOutput, config);
|
|
2012
|
+
spinner.clear().stop();
|
|
2013
|
+
return {
|
|
2014
|
+
output: {
|
|
2015
|
+
manifest: newManifest,
|
|
2016
|
+
steps: [...existingOutput.steps, ...finalOutput.steps],
|
|
2017
|
+
publicAssets: [
|
|
2018
|
+
...existingOutput.publicAssets,
|
|
2019
|
+
...finalOutput.publicAssets
|
|
2020
|
+
]
|
|
2021
|
+
},
|
|
2022
|
+
manifest: newManifest
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// src/core/utils/building/internal-build.ts
|
|
2027
|
+
async function internalBuild(config) {
|
|
2028
|
+
const verb = config.command === "serve" ? "Pre-rendering" : "Building";
|
|
2029
|
+
const target = `${config.browser}-mv${config.manifestVersion}`;
|
|
2030
|
+
config.logger.info(
|
|
2031
|
+
`${verb} ${pc4.cyan(target)} for ${pc4.cyan(config.mode)} with ${pc4.green(
|
|
2032
|
+
`Vite ${vite5.version}`
|
|
2033
|
+
)}`
|
|
2034
|
+
);
|
|
2035
|
+
const startTime = Date.now();
|
|
2036
|
+
await fs11.rm(config.outDir, { recursive: true, force: true });
|
|
2037
|
+
await fs11.ensureDir(config.outDir);
|
|
2038
|
+
const entrypoints = await findEntrypoints(config);
|
|
2039
|
+
config.logger.debug("Detected entrypoints:", entrypoints);
|
|
2040
|
+
const groups = groupEntrypoints(entrypoints);
|
|
2041
|
+
const { output } = await rebuild(config, groups, void 0);
|
|
2042
|
+
await printBuildSummary(
|
|
2043
|
+
config.logger.success,
|
|
2044
|
+
`Built extension in ${formatDuration(Date.now() - startTime)}`,
|
|
2045
|
+
output,
|
|
2046
|
+
config
|
|
2047
|
+
);
|
|
2048
|
+
if (config.analysis.enabled) {
|
|
2049
|
+
await combineAnalysisStats(config);
|
|
2050
|
+
config.logger.info(
|
|
2051
|
+
`Analysis complete:
|
|
2052
|
+
${pc4.gray("\u2514\u2500")} ${pc4.yellow("stats.html")}`
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
return output;
|
|
2056
|
+
}
|
|
2057
|
+
async function combineAnalysisStats(config) {
|
|
2058
|
+
const { execaCommand } = await import("./execa-WKZHVHC5.js");
|
|
2059
|
+
const unixFiles = await glob2(`stats-*.json`, {
|
|
2060
|
+
cwd: config.outDir,
|
|
2061
|
+
absolute: true
|
|
2062
|
+
});
|
|
2063
|
+
const absolutePaths = unixFiles.map(unnormalizePath);
|
|
2064
|
+
await execaCommand(
|
|
2065
|
+
`rollup-plugin-visualizer ${absolutePaths.join(" ")} --template ${config.analysis.template}`,
|
|
2066
|
+
{ cwd: config.root, stdio: "inherit" }
|
|
2067
|
+
);
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/core/utils/building/find-entrypoints.ts
|
|
2071
|
+
import glob3 from "fast-glob";
|
|
2072
|
+
async function findEntrypoints(config) {
|
|
2073
|
+
const relativePaths = await glob3("**/*", {
|
|
2074
|
+
cwd: config.entrypointsDir
|
|
2075
|
+
});
|
|
2076
|
+
relativePaths.sort();
|
|
2077
|
+
const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
|
|
2078
|
+
let hasBackground = false;
|
|
2079
|
+
const possibleEntrypoints = await Promise.all(
|
|
2080
|
+
relativePaths.map(async (relativePath) => {
|
|
2081
|
+
const path7 = resolve12(config.entrypointsDir, relativePath);
|
|
2082
|
+
const matchingGlob = pathGlobs.find(
|
|
2083
|
+
(glob4) => minimatch(relativePath, glob4)
|
|
2084
|
+
);
|
|
2085
|
+
if (matchingGlob == null) {
|
|
2086
|
+
config.logger.warn(
|
|
2087
|
+
`${relativePath} does not match any known entrypoint. Known entrypoints:
|
|
2088
|
+
${JSON.stringify(
|
|
2089
|
+
PATH_GLOB_TO_TYPE_MAP,
|
|
2090
|
+
null,
|
|
2091
|
+
2
|
|
2092
|
+
)}`
|
|
2093
|
+
);
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
|
|
2097
|
+
if (type === "ignored")
|
|
2098
|
+
return;
|
|
2099
|
+
switch (type) {
|
|
2100
|
+
case "popup":
|
|
2101
|
+
return await getPopupEntrypoint(config, path7);
|
|
2102
|
+
case "options":
|
|
2103
|
+
return await getOptionsEntrypoint(config, path7);
|
|
2104
|
+
case "background":
|
|
2105
|
+
hasBackground = true;
|
|
2106
|
+
return await getBackgroundEntrypoint(config, path7);
|
|
2107
|
+
case "content-script":
|
|
2108
|
+
return await getContentScriptEntrypoint(config, path7);
|
|
2109
|
+
case "unlisted-page":
|
|
2110
|
+
return await getUnlistedPageEntrypoint(config, path7);
|
|
2111
|
+
case "unlisted-script":
|
|
2112
|
+
return await getUnlistedScriptEntrypoint(config, path7);
|
|
2113
|
+
case "content-script-style":
|
|
2114
|
+
return {
|
|
2115
|
+
type,
|
|
2116
|
+
name: getEntrypointName(config.entrypointsDir, path7),
|
|
2117
|
+
inputPath: path7,
|
|
2118
|
+
outputDir: resolve12(config.outDir, CONTENT_SCRIPT_OUT_DIR),
|
|
2119
|
+
options: {
|
|
2120
|
+
include: void 0,
|
|
2121
|
+
exclude: void 0
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
default:
|
|
2125
|
+
return {
|
|
2126
|
+
type,
|
|
2127
|
+
name: getEntrypointName(config.entrypointsDir, path7),
|
|
2128
|
+
inputPath: path7,
|
|
2129
|
+
outputDir: config.outDir,
|
|
2130
|
+
options: {
|
|
2131
|
+
include: void 0,
|
|
2132
|
+
exclude: void 0
|
|
2133
|
+
}
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
})
|
|
2137
|
+
);
|
|
2138
|
+
const entrypoints = possibleEntrypoints.filter(
|
|
2139
|
+
(entry) => !!entry
|
|
2140
|
+
);
|
|
2141
|
+
const existingNames = {};
|
|
2142
|
+
entrypoints.forEach((entrypoint) => {
|
|
2143
|
+
const withSameName = existingNames[entrypoint.name];
|
|
2144
|
+
if (withSameName) {
|
|
2145
|
+
throw Error(
|
|
2146
|
+
`Multiple entrypoints with the name "${entrypoint.name}" detected, but only one is allowed: ${[
|
|
2147
|
+
relative4(config.root, withSameName.inputPath),
|
|
2148
|
+
relative4(config.root, entrypoint.inputPath)
|
|
2149
|
+
].join(", ")}`
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
existingNames[entrypoint.name] = entrypoint;
|
|
2153
|
+
});
|
|
2154
|
+
if (config.command === "serve" && !hasBackground) {
|
|
2155
|
+
entrypoints.push(
|
|
2156
|
+
await getBackgroundEntrypoint(config, VIRTUAL_NOOP_BACKGROUND_MODULE_ID)
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
config.logger.debug("All entrypoints:", entrypoints);
|
|
2160
|
+
const targetEntrypoints = entrypoints.filter((entry) => {
|
|
2161
|
+
const { include, exclude } = entry.options;
|
|
2162
|
+
if (include?.length && exclude?.length) {
|
|
2163
|
+
config.logger.warn(
|
|
2164
|
+
`The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`
|
|
2165
|
+
);
|
|
2166
|
+
return false;
|
|
2167
|
+
}
|
|
2168
|
+
if (exclude?.length && !include?.length) {
|
|
2169
|
+
return !exclude.includes(config.browser);
|
|
2170
|
+
}
|
|
2171
|
+
if (include?.length && !exclude?.length) {
|
|
2172
|
+
return include.includes(config.browser);
|
|
2173
|
+
}
|
|
2174
|
+
return true;
|
|
2175
|
+
});
|
|
2176
|
+
config.logger.debug(`${config.browser} entrypoints:`, targetEntrypoints);
|
|
2177
|
+
return targetEntrypoints;
|
|
2178
|
+
}
|
|
2179
|
+
function getHtmlBaseOptions(document) {
|
|
2180
|
+
const options = {};
|
|
2181
|
+
const includeContent = document.querySelector("meta[name='manifest.include']")?.getAttribute("content");
|
|
2182
|
+
if (includeContent) {
|
|
2183
|
+
options.include = JSON5.parse(includeContent);
|
|
2184
|
+
}
|
|
2185
|
+
const excludeContent = document.querySelector("meta[name='manifest.exclude']")?.getAttribute("content");
|
|
2186
|
+
if (excludeContent) {
|
|
2187
|
+
options.exclude = JSON5.parse(excludeContent);
|
|
2188
|
+
}
|
|
2189
|
+
return options;
|
|
2190
|
+
}
|
|
2191
|
+
async function getPopupEntrypoint(config, path7) {
|
|
2192
|
+
const content = await fs12.readFile(path7, "utf-8");
|
|
2193
|
+
const { document } = parseHTML2(content);
|
|
2194
|
+
const options = getHtmlBaseOptions(document);
|
|
2195
|
+
const title = document.querySelector("title");
|
|
2196
|
+
if (title != null)
|
|
2197
|
+
options.defaultTitle = title.textContent ?? void 0;
|
|
2198
|
+
const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
|
|
2199
|
+
if (defaultIconContent) {
|
|
2200
|
+
try {
|
|
2201
|
+
options.defaultIcon = JSON5.parse(defaultIconContent);
|
|
2202
|
+
} catch (err) {
|
|
2203
|
+
config.logger.fatal(
|
|
2204
|
+
`Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
|
|
2205
|
+
err
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
const mv2TypeContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
|
|
2210
|
+
if (mv2TypeContent) {
|
|
2211
|
+
options.mv2Key = mv2TypeContent === "page_action" ? "page_action" : "browser_action";
|
|
2212
|
+
}
|
|
2213
|
+
const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
|
|
2214
|
+
if (browserStyleContent) {
|
|
2215
|
+
options.browserStyle = browserStyleContent === "true";
|
|
2216
|
+
}
|
|
2217
|
+
return {
|
|
2218
|
+
type: "popup",
|
|
2219
|
+
name: "popup",
|
|
2220
|
+
options,
|
|
2221
|
+
inputPath: path7,
|
|
2222
|
+
outputDir: config.outDir
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
async function getOptionsEntrypoint(config, path7) {
|
|
2226
|
+
const content = await fs12.readFile(path7, "utf-8");
|
|
2227
|
+
const { document } = parseHTML2(content);
|
|
2228
|
+
const options = getHtmlBaseOptions(document);
|
|
2229
|
+
const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
|
|
2230
|
+
if (openInTabContent) {
|
|
2231
|
+
options.openInTab = openInTabContent === "true";
|
|
2232
|
+
}
|
|
2233
|
+
const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
|
|
2234
|
+
if (chromeStyleContent) {
|
|
2235
|
+
options.chromeStyle = chromeStyleContent === "true";
|
|
2236
|
+
}
|
|
2237
|
+
const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
|
|
2238
|
+
if (browserStyleContent) {
|
|
2239
|
+
options.browserStyle = browserStyleContent === "true";
|
|
2240
|
+
}
|
|
2241
|
+
return {
|
|
2242
|
+
type: "options",
|
|
2243
|
+
name: "options",
|
|
2244
|
+
options,
|
|
2245
|
+
inputPath: path7,
|
|
2246
|
+
outputDir: config.outDir
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
async function getUnlistedPageEntrypoint(config, path7) {
|
|
2250
|
+
const content = await fs12.readFile(path7, "utf-8");
|
|
2251
|
+
const { document } = parseHTML2(content);
|
|
2252
|
+
return {
|
|
2253
|
+
type: "unlisted-page",
|
|
2254
|
+
name: getEntrypointName(config.entrypointsDir, path7),
|
|
2255
|
+
inputPath: path7,
|
|
2256
|
+
outputDir: config.outDir,
|
|
2257
|
+
options: getHtmlBaseOptions(document)
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
async function getUnlistedScriptEntrypoint(config, path7) {
|
|
2261
|
+
const name = getEntrypointName(config.entrypointsDir, path7);
|
|
2262
|
+
const defaultExport = await importEntrypointFile(
|
|
2263
|
+
path7,
|
|
2264
|
+
config
|
|
2265
|
+
);
|
|
2266
|
+
if (defaultExport == null) {
|
|
2267
|
+
throw Error(
|
|
2268
|
+
`${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
const { main: _, ...moduleOptions } = defaultExport;
|
|
2272
|
+
const options = moduleOptions;
|
|
2273
|
+
return {
|
|
2274
|
+
type: "unlisted-script",
|
|
2275
|
+
name,
|
|
2276
|
+
inputPath: path7,
|
|
2277
|
+
outputDir: config.outDir,
|
|
2278
|
+
options
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
async function getBackgroundEntrypoint(config, path7) {
|
|
2282
|
+
const name = "background";
|
|
2283
|
+
let options = {};
|
|
2284
|
+
if (path7 !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
|
|
2285
|
+
const defaultExport = await importEntrypointFile(
|
|
2286
|
+
path7,
|
|
2287
|
+
config
|
|
2288
|
+
);
|
|
2289
|
+
if (defaultExport == null) {
|
|
2290
|
+
throw Error(
|
|
2291
|
+
`${name}: Default export not found, did you forget to call "export default defineBackground(...)"?`
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
const { main: _, ...moduleOptions } = defaultExport;
|
|
2295
|
+
options = moduleOptions;
|
|
2296
|
+
}
|
|
2297
|
+
return {
|
|
2298
|
+
type: "background",
|
|
2299
|
+
name,
|
|
2300
|
+
inputPath: path7,
|
|
2301
|
+
outputDir: config.outDir,
|
|
2302
|
+
options: {
|
|
2303
|
+
...options,
|
|
2304
|
+
type: resolvePerBrowserOption(options.type, config.browser),
|
|
2305
|
+
persistent: resolvePerBrowserOption(options.persistent, config.browser)
|
|
2306
|
+
}
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
async function getContentScriptEntrypoint(config, path7) {
|
|
2310
|
+
const name = getEntrypointName(config.entrypointsDir, path7);
|
|
2311
|
+
const { main: _, ...options } = await importEntrypointFile(path7, config);
|
|
2312
|
+
if (options == null) {
|
|
2313
|
+
throw Error(
|
|
2314
|
+
`${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
|
|
2315
|
+
);
|
|
2316
|
+
}
|
|
2317
|
+
return {
|
|
2318
|
+
type: "content-script",
|
|
2319
|
+
name,
|
|
2320
|
+
inputPath: path7,
|
|
2321
|
+
outputDir: resolve12(config.outDir, CONTENT_SCRIPT_OUT_DIR),
|
|
2322
|
+
options
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
var PATH_GLOB_TO_TYPE_MAP = {
|
|
2326
|
+
"sandbox.html": "sandbox",
|
|
2327
|
+
"sandbox/index.html": "sandbox",
|
|
2328
|
+
"*.sandbox.html": "sandbox",
|
|
2329
|
+
"*.sandbox/index.html": "sandbox",
|
|
2330
|
+
"bookmarks.html": "bookmarks",
|
|
2331
|
+
"bookmarks/index.html": "bookmarks",
|
|
2332
|
+
"history.html": "history",
|
|
2333
|
+
"history/index.html": "history",
|
|
2334
|
+
"newtab.html": "newtab",
|
|
2335
|
+
"newtab/index.html": "newtab",
|
|
2336
|
+
"sidepanel.html": "sidepanel",
|
|
2337
|
+
"sidepanel/index.html": "sidepanel",
|
|
2338
|
+
"*.sidepanel.html": "sidepanel",
|
|
2339
|
+
"*.sidepanel/index.html": "sidepanel",
|
|
2340
|
+
"devtools.html": "devtools",
|
|
2341
|
+
"devtools/index.html": "devtools",
|
|
2342
|
+
"background.[jt]s": "background",
|
|
2343
|
+
"background/index.[jt]s": "background",
|
|
2344
|
+
[VIRTUAL_NOOP_BACKGROUND_MODULE_ID]: "background",
|
|
2345
|
+
"content.[jt]s?(x)": "content-script",
|
|
2346
|
+
"content/index.[jt]s?(x)": "content-script",
|
|
2347
|
+
"*.content.[jt]s?(x)": "content-script",
|
|
2348
|
+
"*.content/index.[jt]s?(x)": "content-script",
|
|
2349
|
+
[`content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
|
|
2350
|
+
[`*.content.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
|
|
2351
|
+
[`content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
|
|
2352
|
+
[`*.content/index.${CSS_EXTENSIONS_PATTERN}`]: "content-script-style",
|
|
2353
|
+
"popup.html": "popup",
|
|
2354
|
+
"popup/index.html": "popup",
|
|
2355
|
+
"options.html": "options",
|
|
2356
|
+
"options/index.html": "options",
|
|
2357
|
+
"*.html": "unlisted-page",
|
|
2358
|
+
"*/index.html": "unlisted-page",
|
|
2359
|
+
"*.[jt]s": "unlisted-script",
|
|
2360
|
+
"*/index.ts": "unlisted-script",
|
|
2361
|
+
[`*.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
|
|
2362
|
+
[`*/index.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style",
|
|
2363
|
+
// Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files or tests
|
|
2364
|
+
"*/**": "ignored"
|
|
2365
|
+
};
|
|
2366
|
+
var CONTENT_SCRIPT_OUT_DIR = "content-scripts";
|
|
2367
|
+
|
|
2368
|
+
export {
|
|
2369
|
+
getEntrypointOutputFile,
|
|
2370
|
+
getEntrypointBundlePath,
|
|
2371
|
+
resolvePerBrowserOption,
|
|
2372
|
+
formatDuration,
|
|
2373
|
+
download,
|
|
2374
|
+
unimport,
|
|
2375
|
+
tsconfigPaths,
|
|
2376
|
+
globals,
|
|
2377
|
+
webextensionPolyfillAlias,
|
|
2378
|
+
detectDevChanges,
|
|
2379
|
+
findEntrypoints,
|
|
2380
|
+
generateTypesDir,
|
|
2381
|
+
getInternalConfig,
|
|
2382
|
+
kebabCaseAlphanumeric,
|
|
2383
|
+
printFileList,
|
|
2384
|
+
version,
|
|
2385
|
+
getPackageJson,
|
|
2386
|
+
getContentScriptCssFiles,
|
|
2387
|
+
getContentScriptsCssMap,
|
|
2388
|
+
rebuild,
|
|
2389
|
+
internalBuild
|
|
2390
|
+
};
|