wxt 0.16.8 → 0.16.10

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/dist/cli.js CHANGED
@@ -229,6 +229,14 @@ function resolvePerBrowserOption(option, browser) {
229
229
  return option[browser];
230
230
  return option;
231
231
  }
232
+ function resolvePerBrowserOptions(options, browser) {
233
+ return Object.fromEntries(
234
+ Object.entries(options).map(([key, value]) => [
235
+ key,
236
+ key === "defaultIcon" ? value : resolvePerBrowserOption(value, browser)
237
+ ])
238
+ );
239
+ }
232
240
 
233
241
  // src/core/utils/constants.ts
234
242
  var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
@@ -267,6 +275,8 @@ async function findEntrypoints() {
267
275
  switch (type) {
268
276
  case "popup":
269
277
  return await getPopupEntrypoint(info);
278
+ case "sidepanel":
279
+ return await getSidepanelEntrypoint(info);
270
280
  case "options":
271
281
  return await getOptionsEntrypoint(info);
272
282
  case "background":
@@ -377,100 +387,66 @@ function preventNoEntrypoints(files) {
377
387
  throw Error(`No entrypoints found in ${wxt.config.entrypointsDir}`);
378
388
  }
379
389
  }
380
- function getHtmlBaseOptions(document) {
381
- const options = {};
382
- const includeContent = document.querySelector("meta[name='manifest.include']")?.getAttribute("content");
383
- if (includeContent) {
384
- options.include = JSON5.parse(includeContent);
385
- }
386
- const excludeContent = document.querySelector("meta[name='manifest.exclude']")?.getAttribute("content");
387
- if (excludeContent) {
388
- options.exclude = JSON5.parse(excludeContent);
389
- }
390
- return options;
391
- }
392
- async function getPopupEntrypoint({
393
- inputPath,
394
- name,
395
- skipped
396
- }) {
397
- const content = await fs3.readFile(inputPath, "utf-8");
398
- const { document } = parseHTML(content);
399
- const options = getHtmlBaseOptions(document);
400
- const title = document.querySelector("title");
401
- if (title != null)
402
- options.defaultTitle = title.textContent ?? void 0;
403
- const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
404
- if (defaultIconContent) {
405
- try {
406
- options.defaultIcon = JSON5.parse(defaultIconContent);
407
- } catch (err) {
408
- wxt.logger.fatal(
409
- `Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
410
- err
411
- );
390
+ async function getPopupEntrypoint(info) {
391
+ const options = await getHtmlEntrypointOptions(
392
+ info,
393
+ {
394
+ browserStyle: "browse_style",
395
+ exclude: "exclude",
396
+ include: "include",
397
+ defaultIcon: "default_icon",
398
+ defaultTitle: "default_title",
399
+ mv2Key: "type"
400
+ },
401
+ {
402
+ defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
403
+ },
404
+ {
405
+ defaultTitle: (content) => content,
406
+ mv2Key: (content) => content === "page_action" ? "page_action" : "browser_action"
412
407
  }
413
- }
414
- const mv2TypeContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
415
- if (mv2TypeContent) {
416
- options.mv2Key = mv2TypeContent === "page_action" ? "page_action" : "browser_action";
417
- }
418
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
419
- if (browserStyleContent) {
420
- options.browserStyle = browserStyleContent === "true";
421
- }
408
+ );
422
409
  return {
423
410
  type: "popup",
424
411
  name: "popup",
425
- options,
426
- inputPath,
412
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
413
+ inputPath: info.inputPath,
427
414
  outputDir: wxt.config.outDir,
428
- skipped
415
+ skipped: info.skipped
429
416
  };
430
417
  }
431
- async function getOptionsEntrypoint({
432
- inputPath,
433
- name,
434
- skipped
435
- }) {
436
- const content = await fs3.readFile(inputPath, "utf-8");
437
- const { document } = parseHTML(content);
438
- const options = getHtmlBaseOptions(document);
439
- const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
440
- if (openInTabContent) {
441
- options.openInTab = openInTabContent === "true";
442
- }
443
- const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
444
- if (chromeStyleContent) {
445
- options.chromeStyle = chromeStyleContent === "true";
446
- }
447
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
448
- if (browserStyleContent) {
449
- options.browserStyle = browserStyleContent === "true";
450
- }
418
+ async function getOptionsEntrypoint(info) {
419
+ const options = await getHtmlEntrypointOptions(
420
+ info,
421
+ {
422
+ browserStyle: "browse_style",
423
+ chromeStyle: "chrome_style",
424
+ exclude: "exclude",
425
+ include: "include",
426
+ openInTab: "open_in_tab"
427
+ }
428
+ );
451
429
  return {
452
430
  type: "options",
453
431
  name: "options",
454
- options,
455
- inputPath,
432
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
433
+ inputPath: info.inputPath,
456
434
  outputDir: wxt.config.outDir,
457
- skipped
435
+ skipped: info.skipped
458
436
  };
459
437
  }
460
- async function getUnlistedPageEntrypoint({
461
- inputPath,
462
- name,
463
- skipped
464
- }) {
465
- const content = await fs3.readFile(inputPath, "utf-8");
466
- const { document } = parseHTML(content);
438
+ async function getUnlistedPageEntrypoint(info) {
439
+ const options = await getHtmlEntrypointOptions(info, {
440
+ exclude: "exclude",
441
+ include: "include"
442
+ });
467
443
  return {
468
444
  type: "unlisted-page",
469
- name: getEntrypointName(wxt.config.entrypointsDir, inputPath),
470
- inputPath,
445
+ name: info.name,
446
+ inputPath: info.inputPath,
471
447
  outputDir: wxt.config.outDir,
472
- options: getHtmlBaseOptions(document),
473
- skipped
448
+ options,
449
+ skipped: info.skipped
474
450
  };
475
451
  }
476
452
  async function getUnlistedScriptEntrypoint({
@@ -484,14 +460,13 @@ async function getUnlistedScriptEntrypoint({
484
460
  `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
485
461
  );
486
462
  }
487
- const { main: _, ...moduleOptions } = defaultExport;
488
- const options = moduleOptions;
463
+ const { main: _, ...options } = defaultExport;
489
464
  return {
490
465
  type: "unlisted-script",
491
466
  name,
492
467
  inputPath,
493
468
  outputDir: wxt.config.outDir,
494
- options,
469
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
495
470
  skipped
496
471
  };
497
472
  }
@@ -519,14 +494,7 @@ async function getBackgroundEntrypoint({
519
494
  name,
520
495
  inputPath,
521
496
  outputDir: wxt.config.outDir,
522
- options: {
523
- ...options,
524
- type: resolvePerBrowserOption(options.type, wxt.config.browser),
525
- persistent: resolvePerBrowserOption(
526
- options.persistent,
527
- wxt.config.browser
528
- )
529
- },
497
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
530
498
  skipped
531
499
  };
532
500
  }
@@ -546,10 +514,58 @@ async function getContentScriptEntrypoint({
546
514
  name,
547
515
  inputPath,
548
516
  outputDir: resolve3(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
549
- options,
517
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
550
518
  skipped
551
519
  };
552
520
  }
521
+ async function getSidepanelEntrypoint(info) {
522
+ const options = await getHtmlEntrypointOptions(
523
+ info,
524
+ {
525
+ browserStyle: "browse_style",
526
+ exclude: "exclude",
527
+ include: "include",
528
+ defaultIcon: "default_icon",
529
+ defaultTitle: "default_title",
530
+ openAtInstall: "open_at_install"
531
+ },
532
+ {
533
+ defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
534
+ },
535
+ {
536
+ defaultTitle: (content) => content
537
+ }
538
+ );
539
+ return {
540
+ type: "sidepanel",
541
+ name: info.name,
542
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
543
+ inputPath: info.inputPath,
544
+ outputDir: wxt.config.outDir,
545
+ skipped: info.skipped
546
+ };
547
+ }
548
+ async function getHtmlEntrypointOptions(info, keyMap, queries, parsers) {
549
+ const content = await fs3.readFile(info.inputPath, "utf-8");
550
+ const { document } = parseHTML(content);
551
+ const options = {};
552
+ const defaultQuery = (manifestKey) => document.querySelector(`meta[name='manifest.${manifestKey}']`)?.getAttribute("content");
553
+ Object.entries(keyMap).forEach(([_key, manifestKey]) => {
554
+ const key = _key;
555
+ const content2 = queries?.[key] ? queries[key](document, manifestKey) : defaultQuery(manifestKey);
556
+ if (content2) {
557
+ try {
558
+ options[key] = (parsers?.[key] ?? JSON5.parse)(content2);
559
+ } catch (err) {
560
+ wxt.logger.fatal(
561
+ `Failed to parse meta tag content. Usually this means you have invalid JSON5 content (content=${content2})`,
562
+ err
563
+ );
564
+ }
565
+ }
566
+ });
567
+ return options;
568
+ }
553
569
  var PATH_GLOB_TO_TYPE_MAP = {
554
570
  "sandbox.html": "sandbox",
555
571
  "sandbox/index.html": "sandbox",
@@ -1655,6 +1671,7 @@ async function resolveConfig(inlineConfig, command, server) {
1655
1671
  let userConfigMetadata;
1656
1672
  if (inlineConfig.configFile !== false) {
1657
1673
  const { config: loadedConfig, ...metadata } = await loadConfig({
1674
+ configFile: inlineConfig.configFile,
1658
1675
  name: "wxt",
1659
1676
  cwd: inlineConfig.root ?? process.cwd(),
1660
1677
  rcFile: false,
@@ -2033,7 +2050,12 @@ function getEsbuildOptions(opts) {
2033
2050
  return {
2034
2051
  format: "cjs",
2035
2052
  loader: isJsx ? "tsx" : "ts",
2036
- jsx: isJsx ? "automatic" : void 0
2053
+ ...isJsx ? {
2054
+ // `h` and `Fragment` are undefined, but that's OK because JSX is never evaluated while
2055
+ // grabbing the entrypoint's options.
2056
+ jsxFactory: "h",
2057
+ jsxFragment: "Fragment"
2058
+ } : void 0
2037
2059
  };
2038
2060
  }
2039
2061
 
@@ -2152,7 +2174,7 @@ function getChunkSortWeight(filename) {
2152
2174
  import pc4 from "picocolors";
2153
2175
 
2154
2176
  // package.json
2155
- var version = "0.16.8";
2177
+ var version = "0.16.10";
2156
2178
 
2157
2179
  // src/core/utils/log/printHeader.ts
2158
2180
  import { consola as consola2 } from "consola";
@@ -2213,7 +2235,11 @@ var ContentSecurityPolicy = class _ContentSecurityPolicy {
2213
2235
 
2214
2236
  // src/core/utils/content-scripts.ts
2215
2237
  function hashContentScriptOptions(options) {
2216
- const simplifiedOptions = mapWxtOptionsToContentScript(options);
2238
+ const simplifiedOptions = mapWxtOptionsToContentScript(
2239
+ options,
2240
+ void 0,
2241
+ void 0
2242
+ );
2217
2243
  Object.keys(simplifiedOptions).forEach((key) => {
2218
2244
  if (simplifiedOptions[key] == null)
2219
2245
  delete simplifiedOptions[key];
@@ -2239,32 +2265,31 @@ function hashContentScriptOptions(options) {
2239
2265
  }).sort((l, r) => l[0].localeCompare(r[0]))
2240
2266
  );
2241
2267
  }
2242
- function mapWxtOptionsToContentScript(options) {
2268
+ function mapWxtOptionsToContentScript(options, js, css) {
2243
2269
  return {
2244
- matches: resolvePerBrowserOption(options.matches, wxt.config.browser),
2245
- all_frames: resolvePerBrowserOption(options.allFrames, wxt.config.browser),
2246
- match_about_blank: resolvePerBrowserOption(
2247
- options.matchAboutBlank,
2248
- wxt.config.browser
2249
- ),
2250
- exclude_globs: resolvePerBrowserOption(
2251
- options.excludeGlobs,
2252
- wxt.config.browser
2253
- ),
2254
- exclude_matches: resolvePerBrowserOption(
2255
- options.excludeMatches,
2256
- wxt.config.browser
2257
- ),
2258
- include_globs: resolvePerBrowserOption(
2259
- options.includeGlobs,
2260
- wxt.config.browser
2261
- ),
2262
- run_at: resolvePerBrowserOption(options.runAt, wxt.config.browser),
2270
+ matches: options.matches,
2271
+ all_frames: options.allFrames,
2272
+ match_about_blank: options.matchAboutBlank,
2273
+ exclude_globs: options.excludeGlobs,
2274
+ exclude_matches: options.excludeMatches,
2275
+ include_globs: options.includeGlobs,
2276
+ run_at: options.runAt,
2277
+ css,
2278
+ js,
2263
2279
  // @ts-expect-error: untyped chrome options
2264
- match_origin_as_fallback: resolvePerBrowserOption(
2265
- options.matchOriginAsFallback,
2266
- wxt.config.browser
2267
- ),
2280
+ match_origin_as_fallback: options.matchOriginAsFallback,
2281
+ world: options.world
2282
+ };
2283
+ }
2284
+ function mapWxtOptionsToRegisteredContentScript(options, js, css) {
2285
+ return {
2286
+ allFrames: options.allFrames,
2287
+ excludeMatches: options.excludeMatches,
2288
+ matches: options.matches,
2289
+ runAt: options.runAt,
2290
+ js,
2291
+ css,
2292
+ // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
2268
2293
  world: options.world
2269
2294
  };
2270
2295
  }
@@ -2499,9 +2524,11 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
2499
2524
  );
2500
2525
  if (wxt.config.browser === "firefox") {
2501
2526
  manifest.sidebar_action = {
2502
- // TODO: Add options to side panel
2503
- // ...defaultSidepanel.options,
2504
- default_panel: page
2527
+ default_panel: page,
2528
+ browser_style: defaultSidepanel.options.browserStyle,
2529
+ default_icon: defaultSidepanel.options.defaultIcon,
2530
+ default_title: defaultSidepanel.options.defaultTitle,
2531
+ open_at_install: defaultSidepanel.options.openAtInstall
2505
2532
  };
2506
2533
  } else if (wxt.config.manifestVersion === 3) {
2507
2534
  manifest.side_panel = {
@@ -2516,21 +2543,13 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
2516
2543
  if (contentScripts?.length) {
2517
2544
  const cssMap = getContentScriptsCssMap(buildOutput, contentScripts);
2518
2545
  if (wxt.config.command === "serve" && wxt.config.manifestVersion === 3) {
2519
- const hostPermissions = new Set(manifest.host_permissions ?? []);
2520
2546
  contentScripts.forEach((script) => {
2521
- const matches = resolvePerBrowserOption(
2522
- script.options.matches,
2523
- wxt.config.browser
2524
- );
2525
- matches.forEach((matchPattern) => {
2526
- hostPermissions.add(matchPattern);
2547
+ script.options.matches.forEach((matchPattern) => {
2548
+ addHostPermission(manifest, matchPattern);
2527
2549
  });
2528
2550
  });
2529
- hostPermissions.forEach(
2530
- (permission) => addHostPermission(manifest, permission)
2531
- );
2532
2551
  } else {
2533
- const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
2552
+ const hashToEntrypointsMap = contentScripts.filter((cs) => cs.options.registration !== "runtime").reduce((map, script) => {
2534
2553
  const hash = hashContentScriptOptions(script.options);
2535
2554
  if (map.has(hash))
2536
2555
  map.get(hash)?.push(script);
@@ -2538,19 +2557,34 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
2538
2557
  map.set(hash, [script]);
2539
2558
  return map;
2540
2559
  }, /* @__PURE__ */ new Map());
2541
- const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
2542
- ([, scripts]) => ({
2543
- ...mapWxtOptionsToContentScript(scripts[0].options),
2544
- css: getContentScriptCssFiles(scripts, cssMap),
2545
- js: scripts.map(
2560
+ const manifestContentScripts = Array.from(
2561
+ hashToEntrypointsMap.values()
2562
+ ).map(
2563
+ (scripts) => mapWxtOptionsToContentScript(
2564
+ scripts[0].options,
2565
+ scripts.map(
2546
2566
  (entry) => getEntrypointBundlePath(entry, wxt.config.outDir, ".js")
2547
- )
2548
- })
2567
+ ),
2568
+ getContentScriptCssFiles(scripts, cssMap)
2569
+ )
2549
2570
  );
2550
- if (newContentScripts.length >= 0) {
2571
+ if (manifestContentScripts.length >= 0) {
2551
2572
  manifest.content_scripts ??= [];
2552
- manifest.content_scripts.push(...newContentScripts);
2573
+ manifest.content_scripts.push(...manifestContentScripts);
2553
2574
  }
2575
+ const runtimeContentScripts = contentScripts.filter(
2576
+ (cs) => cs.options.registration === "runtime"
2577
+ );
2578
+ if (runtimeContentScripts.length > 0 && wxt.config.manifestVersion === 2) {
2579
+ throw Error(
2580
+ 'Cannot use `registration: "runtime"` with MV2 content scripts, it is a MV3-only feature.'
2581
+ );
2582
+ }
2583
+ runtimeContentScripts.forEach((script) => {
2584
+ script.options.matches.forEach((matchPattern) => {
2585
+ addHostPermission(manifest, matchPattern);
2586
+ });
2587
+ });
2554
2588
  }
2555
2589
  const contentScriptCssResources = getContentScriptCssWebAccessibleResources(
2556
2590
  contentScripts,
@@ -2649,10 +2683,9 @@ function getContentScriptCssWebAccessibleResources(contentScripts, contentScript
2649
2683
  return;
2650
2684
  resources.push({
2651
2685
  resources: [cssFile],
2652
- matches: resolvePerBrowserOption(
2653
- script.options.matches,
2654
- wxt.config.browser
2655
- ).map((matchPattern) => stripPathFromMatchPattern(matchPattern))
2686
+ matches: script.options.matches.map(
2687
+ (matchPattern) => stripPathFromMatchPattern(matchPattern)
2688
+ )
2656
2689
  });
2657
2690
  });
2658
2691
  return resources;
@@ -3266,25 +3299,9 @@ function reloadContentScripts(steps, server) {
3266
3299
  const js = [getEntrypointBundlePath(entry, wxt.config.outDir, ".js")];
3267
3300
  const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
3268
3301
  const css = getContentScriptCssFiles([entry], cssMap);
3269
- server.reloadContentScript({
3270
- allFrames: resolvePerBrowserOption(
3271
- entry.options.allFrames,
3272
- wxt.config.browser
3273
- ),
3274
- excludeMatches: resolvePerBrowserOption(
3275
- entry.options.excludeMatches,
3276
- wxt.config.browser
3277
- ),
3278
- matches: resolvePerBrowserOption(
3279
- entry.options.matches,
3280
- wxt.config.browser
3281
- ),
3282
- runAt: resolvePerBrowserOption(entry.options.runAt, wxt.config.browser),
3283
- // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
3284
- world: resolvePerBrowserOption(entry.options.world, wxt.config.browser),
3285
- js,
3286
- css
3287
- });
3302
+ server.reloadContentScript(
3303
+ mapWxtOptionsToRegisteredContentScript(entry.options, js, css)
3304
+ );
3288
3305
  });
3289
3306
  } else {
3290
3307
  server.reloadExtension();
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as ContentScriptContext } from './index-TYmXqKVq.js';
1
+ import { a as ContentScriptContext } from './index-v_64CCcw.js';
2
2
  import 'webextension-polyfill';
3
3
 
4
4
  interface IntegratedContentScriptUi<TMounted> extends ContentScriptUi<TMounted> {
package/dist/client.js CHANGED
@@ -299,13 +299,13 @@ async function createShadowRootUi(ctx, options) {
299
299
  shadowHost.setAttribute("data-wxt-shadow-root", "");
300
300
  let mounted;
301
301
  const mount = () => {
302
- mounted = options.onMount(uiContainer, shadow, shadowHost);
303
302
  mountUi(shadowHost, options);
304
303
  applyPosition(shadowHost, shadow.querySelector("html"), options);
304
+ mounted = options.onMount(uiContainer, shadow, shadowHost);
305
305
  };
306
306
  const remove = () => {
307
- shadowHost.remove();
308
307
  options.onRemove?.(mounted);
308
+ shadowHost.remove();
309
309
  while (uiContainer.lastChild)
310
310
  uiContainer.removeChild(uiContainer.lastChild);
311
311
  };