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.
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.16.8";
2
+ var version = "0.16.10";
3
3
 
4
4
  // src/core/utils/paths.ts
5
5
  import systemPath from "node:path";
@@ -204,6 +204,14 @@ function resolvePerBrowserOption(option, browser) {
204
204
  return option[browser];
205
205
  return option;
206
206
  }
207
+ function resolvePerBrowserOptions(options, browser) {
208
+ return Object.fromEntries(
209
+ Object.entries(options).map(([key, value]) => [
210
+ key,
211
+ key === "defaultIcon" ? value : resolvePerBrowserOption(value, browser)
212
+ ])
213
+ );
214
+ }
207
215
 
208
216
  // src/core/utils/constants.ts
209
217
  var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
@@ -242,6 +250,8 @@ async function findEntrypoints() {
242
250
  switch (type) {
243
251
  case "popup":
244
252
  return await getPopupEntrypoint(info);
253
+ case "sidepanel":
254
+ return await getSidepanelEntrypoint(info);
245
255
  case "options":
246
256
  return await getOptionsEntrypoint(info);
247
257
  case "background":
@@ -352,100 +362,66 @@ function preventNoEntrypoints(files) {
352
362
  throw Error(`No entrypoints found in ${wxt.config.entrypointsDir}`);
353
363
  }
354
364
  }
355
- function getHtmlBaseOptions(document) {
356
- const options = {};
357
- const includeContent = document.querySelector("meta[name='manifest.include']")?.getAttribute("content");
358
- if (includeContent) {
359
- options.include = JSON5.parse(includeContent);
360
- }
361
- const excludeContent = document.querySelector("meta[name='manifest.exclude']")?.getAttribute("content");
362
- if (excludeContent) {
363
- options.exclude = JSON5.parse(excludeContent);
364
- }
365
- return options;
366
- }
367
- async function getPopupEntrypoint({
368
- inputPath,
369
- name,
370
- skipped
371
- }) {
372
- const content = await fs3.readFile(inputPath, "utf-8");
373
- const { document } = parseHTML(content);
374
- const options = getHtmlBaseOptions(document);
375
- const title = document.querySelector("title");
376
- if (title != null)
377
- options.defaultTitle = title.textContent ?? void 0;
378
- const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
379
- if (defaultIconContent) {
380
- try {
381
- options.defaultIcon = JSON5.parse(defaultIconContent);
382
- } catch (err) {
383
- wxt.logger.fatal(
384
- `Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
385
- err
386
- );
365
+ async function getPopupEntrypoint(info) {
366
+ const options = await getHtmlEntrypointOptions(
367
+ info,
368
+ {
369
+ browserStyle: "browse_style",
370
+ exclude: "exclude",
371
+ include: "include",
372
+ defaultIcon: "default_icon",
373
+ defaultTitle: "default_title",
374
+ mv2Key: "type"
375
+ },
376
+ {
377
+ defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
378
+ },
379
+ {
380
+ defaultTitle: (content) => content,
381
+ mv2Key: (content) => content === "page_action" ? "page_action" : "browser_action"
387
382
  }
388
- }
389
- const mv2TypeContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
390
- if (mv2TypeContent) {
391
- options.mv2Key = mv2TypeContent === "page_action" ? "page_action" : "browser_action";
392
- }
393
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
394
- if (browserStyleContent) {
395
- options.browserStyle = browserStyleContent === "true";
396
- }
383
+ );
397
384
  return {
398
385
  type: "popup",
399
386
  name: "popup",
400
- options,
401
- inputPath,
387
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
388
+ inputPath: info.inputPath,
402
389
  outputDir: wxt.config.outDir,
403
- skipped
390
+ skipped: info.skipped
404
391
  };
405
392
  }
406
- async function getOptionsEntrypoint({
407
- inputPath,
408
- name,
409
- skipped
410
- }) {
411
- const content = await fs3.readFile(inputPath, "utf-8");
412
- const { document } = parseHTML(content);
413
- const options = getHtmlBaseOptions(document);
414
- const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
415
- if (openInTabContent) {
416
- options.openInTab = openInTabContent === "true";
417
- }
418
- const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
419
- if (chromeStyleContent) {
420
- options.chromeStyle = chromeStyleContent === "true";
421
- }
422
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
423
- if (browserStyleContent) {
424
- options.browserStyle = browserStyleContent === "true";
425
- }
393
+ async function getOptionsEntrypoint(info) {
394
+ const options = await getHtmlEntrypointOptions(
395
+ info,
396
+ {
397
+ browserStyle: "browse_style",
398
+ chromeStyle: "chrome_style",
399
+ exclude: "exclude",
400
+ include: "include",
401
+ openInTab: "open_in_tab"
402
+ }
403
+ );
426
404
  return {
427
405
  type: "options",
428
406
  name: "options",
429
- options,
430
- inputPath,
407
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
408
+ inputPath: info.inputPath,
431
409
  outputDir: wxt.config.outDir,
432
- skipped
410
+ skipped: info.skipped
433
411
  };
434
412
  }
435
- async function getUnlistedPageEntrypoint({
436
- inputPath,
437
- name,
438
- skipped
439
- }) {
440
- const content = await fs3.readFile(inputPath, "utf-8");
441
- const { document } = parseHTML(content);
413
+ async function getUnlistedPageEntrypoint(info) {
414
+ const options = await getHtmlEntrypointOptions(info, {
415
+ exclude: "exclude",
416
+ include: "include"
417
+ });
442
418
  return {
443
419
  type: "unlisted-page",
444
- name: getEntrypointName(wxt.config.entrypointsDir, inputPath),
445
- inputPath,
420
+ name: info.name,
421
+ inputPath: info.inputPath,
446
422
  outputDir: wxt.config.outDir,
447
- options: getHtmlBaseOptions(document),
448
- skipped
423
+ options,
424
+ skipped: info.skipped
449
425
  };
450
426
  }
451
427
  async function getUnlistedScriptEntrypoint({
@@ -459,14 +435,13 @@ async function getUnlistedScriptEntrypoint({
459
435
  `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
460
436
  );
461
437
  }
462
- const { main: _, ...moduleOptions } = defaultExport;
463
- const options = moduleOptions;
438
+ const { main: _, ...options } = defaultExport;
464
439
  return {
465
440
  type: "unlisted-script",
466
441
  name,
467
442
  inputPath,
468
443
  outputDir: wxt.config.outDir,
469
- options,
444
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
470
445
  skipped
471
446
  };
472
447
  }
@@ -494,14 +469,7 @@ async function getBackgroundEntrypoint({
494
469
  name,
495
470
  inputPath,
496
471
  outputDir: wxt.config.outDir,
497
- options: {
498
- ...options,
499
- type: resolvePerBrowserOption(options.type, wxt.config.browser),
500
- persistent: resolvePerBrowserOption(
501
- options.persistent,
502
- wxt.config.browser
503
- )
504
- },
472
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
505
473
  skipped
506
474
  };
507
475
  }
@@ -521,10 +489,58 @@ async function getContentScriptEntrypoint({
521
489
  name,
522
490
  inputPath,
523
491
  outputDir: resolve3(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
524
- options,
492
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
525
493
  skipped
526
494
  };
527
495
  }
496
+ async function getSidepanelEntrypoint(info) {
497
+ const options = await getHtmlEntrypointOptions(
498
+ info,
499
+ {
500
+ browserStyle: "browse_style",
501
+ exclude: "exclude",
502
+ include: "include",
503
+ defaultIcon: "default_icon",
504
+ defaultTitle: "default_title",
505
+ openAtInstall: "open_at_install"
506
+ },
507
+ {
508
+ defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
509
+ },
510
+ {
511
+ defaultTitle: (content) => content
512
+ }
513
+ );
514
+ return {
515
+ type: "sidepanel",
516
+ name: info.name,
517
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
518
+ inputPath: info.inputPath,
519
+ outputDir: wxt.config.outDir,
520
+ skipped: info.skipped
521
+ };
522
+ }
523
+ async function getHtmlEntrypointOptions(info, keyMap, queries, parsers) {
524
+ const content = await fs3.readFile(info.inputPath, "utf-8");
525
+ const { document } = parseHTML(content);
526
+ const options = {};
527
+ const defaultQuery = (manifestKey) => document.querySelector(`meta[name='manifest.${manifestKey}']`)?.getAttribute("content");
528
+ Object.entries(keyMap).forEach(([_key, manifestKey]) => {
529
+ const key = _key;
530
+ const content2 = queries?.[key] ? queries[key](document, manifestKey) : defaultQuery(manifestKey);
531
+ if (content2) {
532
+ try {
533
+ options[key] = (parsers?.[key] ?? JSON5.parse)(content2);
534
+ } catch (err) {
535
+ wxt.logger.fatal(
536
+ `Failed to parse meta tag content. Usually this means you have invalid JSON5 content (content=${content2})`,
537
+ err
538
+ );
539
+ }
540
+ }
541
+ });
542
+ return options;
543
+ }
528
544
  var PATH_GLOB_TO_TYPE_MAP = {
529
545
  "sandbox.html": "sandbox",
530
546
  "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
 
@@ -2204,7 +2226,11 @@ var ContentSecurityPolicy = class _ContentSecurityPolicy {
2204
2226
 
2205
2227
  // src/core/utils/content-scripts.ts
2206
2228
  function hashContentScriptOptions(options) {
2207
- const simplifiedOptions = mapWxtOptionsToContentScript(options);
2229
+ const simplifiedOptions = mapWxtOptionsToContentScript(
2230
+ options,
2231
+ void 0,
2232
+ void 0
2233
+ );
2208
2234
  Object.keys(simplifiedOptions).forEach((key) => {
2209
2235
  if (simplifiedOptions[key] == null)
2210
2236
  delete simplifiedOptions[key];
@@ -2230,32 +2256,31 @@ function hashContentScriptOptions(options) {
2230
2256
  }).sort((l, r) => l[0].localeCompare(r[0]))
2231
2257
  );
2232
2258
  }
2233
- function mapWxtOptionsToContentScript(options) {
2259
+ function mapWxtOptionsToContentScript(options, js, css) {
2234
2260
  return {
2235
- matches: resolvePerBrowserOption(options.matches, wxt.config.browser),
2236
- all_frames: resolvePerBrowserOption(options.allFrames, wxt.config.browser),
2237
- match_about_blank: resolvePerBrowserOption(
2238
- options.matchAboutBlank,
2239
- wxt.config.browser
2240
- ),
2241
- exclude_globs: resolvePerBrowserOption(
2242
- options.excludeGlobs,
2243
- wxt.config.browser
2244
- ),
2245
- exclude_matches: resolvePerBrowserOption(
2246
- options.excludeMatches,
2247
- wxt.config.browser
2248
- ),
2249
- include_globs: resolvePerBrowserOption(
2250
- options.includeGlobs,
2251
- wxt.config.browser
2252
- ),
2253
- run_at: resolvePerBrowserOption(options.runAt, wxt.config.browser),
2261
+ matches: options.matches,
2262
+ all_frames: options.allFrames,
2263
+ match_about_blank: options.matchAboutBlank,
2264
+ exclude_globs: options.excludeGlobs,
2265
+ exclude_matches: options.excludeMatches,
2266
+ include_globs: options.includeGlobs,
2267
+ run_at: options.runAt,
2268
+ css,
2269
+ js,
2254
2270
  // @ts-expect-error: untyped chrome options
2255
- match_origin_as_fallback: resolvePerBrowserOption(
2256
- options.matchOriginAsFallback,
2257
- wxt.config.browser
2258
- ),
2271
+ match_origin_as_fallback: options.matchOriginAsFallback,
2272
+ world: options.world
2273
+ };
2274
+ }
2275
+ function mapWxtOptionsToRegisteredContentScript(options, js, css) {
2276
+ return {
2277
+ allFrames: options.allFrames,
2278
+ excludeMatches: options.excludeMatches,
2279
+ matches: options.matches,
2280
+ runAt: options.runAt,
2281
+ js,
2282
+ css,
2283
+ // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
2259
2284
  world: options.world
2260
2285
  };
2261
2286
  }
@@ -2490,9 +2515,11 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
2490
2515
  );
2491
2516
  if (wxt.config.browser === "firefox") {
2492
2517
  manifest.sidebar_action = {
2493
- // TODO: Add options to side panel
2494
- // ...defaultSidepanel.options,
2495
- default_panel: page
2518
+ default_panel: page,
2519
+ browser_style: defaultSidepanel.options.browserStyle,
2520
+ default_icon: defaultSidepanel.options.defaultIcon,
2521
+ default_title: defaultSidepanel.options.defaultTitle,
2522
+ open_at_install: defaultSidepanel.options.openAtInstall
2496
2523
  };
2497
2524
  } else if (wxt.config.manifestVersion === 3) {
2498
2525
  manifest.side_panel = {
@@ -2507,21 +2534,13 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
2507
2534
  if (contentScripts?.length) {
2508
2535
  const cssMap = getContentScriptsCssMap(buildOutput, contentScripts);
2509
2536
  if (wxt.config.command === "serve" && wxt.config.manifestVersion === 3) {
2510
- const hostPermissions = new Set(manifest.host_permissions ?? []);
2511
2537
  contentScripts.forEach((script) => {
2512
- const matches = resolvePerBrowserOption(
2513
- script.options.matches,
2514
- wxt.config.browser
2515
- );
2516
- matches.forEach((matchPattern) => {
2517
- hostPermissions.add(matchPattern);
2538
+ script.options.matches.forEach((matchPattern) => {
2539
+ addHostPermission(manifest, matchPattern);
2518
2540
  });
2519
2541
  });
2520
- hostPermissions.forEach(
2521
- (permission) => addHostPermission(manifest, permission)
2522
- );
2523
2542
  } else {
2524
- const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
2543
+ const hashToEntrypointsMap = contentScripts.filter((cs) => cs.options.registration !== "runtime").reduce((map, script) => {
2525
2544
  const hash = hashContentScriptOptions(script.options);
2526
2545
  if (map.has(hash))
2527
2546
  map.get(hash)?.push(script);
@@ -2529,19 +2548,34 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
2529
2548
  map.set(hash, [script]);
2530
2549
  return map;
2531
2550
  }, /* @__PURE__ */ new Map());
2532
- const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
2533
- ([, scripts]) => ({
2534
- ...mapWxtOptionsToContentScript(scripts[0].options),
2535
- css: getContentScriptCssFiles(scripts, cssMap),
2536
- js: scripts.map(
2551
+ const manifestContentScripts = Array.from(
2552
+ hashToEntrypointsMap.values()
2553
+ ).map(
2554
+ (scripts) => mapWxtOptionsToContentScript(
2555
+ scripts[0].options,
2556
+ scripts.map(
2537
2557
  (entry) => getEntrypointBundlePath(entry, wxt.config.outDir, ".js")
2538
- )
2539
- })
2558
+ ),
2559
+ getContentScriptCssFiles(scripts, cssMap)
2560
+ )
2540
2561
  );
2541
- if (newContentScripts.length >= 0) {
2562
+ if (manifestContentScripts.length >= 0) {
2542
2563
  manifest.content_scripts ??= [];
2543
- manifest.content_scripts.push(...newContentScripts);
2564
+ manifest.content_scripts.push(...manifestContentScripts);
2544
2565
  }
2566
+ const runtimeContentScripts = contentScripts.filter(
2567
+ (cs) => cs.options.registration === "runtime"
2568
+ );
2569
+ if (runtimeContentScripts.length > 0 && wxt.config.manifestVersion === 2) {
2570
+ throw Error(
2571
+ 'Cannot use `registration: "runtime"` with MV2 content scripts, it is a MV3-only feature.'
2572
+ );
2573
+ }
2574
+ runtimeContentScripts.forEach((script) => {
2575
+ script.options.matches.forEach((matchPattern) => {
2576
+ addHostPermission(manifest, matchPattern);
2577
+ });
2578
+ });
2545
2579
  }
2546
2580
  const contentScriptCssResources = getContentScriptCssWebAccessibleResources(
2547
2581
  contentScripts,
@@ -2640,10 +2674,9 @@ function getContentScriptCssWebAccessibleResources(contentScripts, contentScript
2640
2674
  return;
2641
2675
  resources.push({
2642
2676
  resources: [cssFile],
2643
- matches: resolvePerBrowserOption(
2644
- script.options.matches,
2645
- wxt.config.browser
2646
- ).map((matchPattern) => stripPathFromMatchPattern(matchPattern))
2677
+ matches: script.options.matches.map(
2678
+ (matchPattern) => stripPathFromMatchPattern(matchPattern)
2679
+ )
2647
2680
  });
2648
2681
  });
2649
2682
  return resources;
@@ -2943,7 +2976,6 @@ export {
2943
2976
  registerWxt,
2944
2977
  detectDevChanges,
2945
2978
  getEntrypointBundlePath,
2946
- resolvePerBrowserOption,
2947
2979
  findEntrypoints,
2948
2980
  generateTypesDir,
2949
2981
  formatDuration,
@@ -2957,6 +2989,7 @@ export {
2957
2989
  kebabCaseAlphanumeric,
2958
2990
  printFileList,
2959
2991
  version,
2992
+ mapWxtOptionsToRegisteredContentScript,
2960
2993
  getContentScriptCssFiles,
2961
2994
  getContentScriptsCssMap,
2962
2995
  rebuild,