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.
@@ -97,29 +97,35 @@ interface WxtContentScriptEventMap extends WindowEventMap {
97
97
  }
98
98
 
99
99
  type TargetBrowser = string;
100
- type ContentScriptDefinition = ContentScriptIsolatedWorldDefinition | ContentScriptMainWorldDefinition;
101
- interface ContentScriptIsolatedWorldDefinition extends ContentScriptBaseDefinition {
100
+ interface BaseEntrypointOptions {
102
101
  /**
103
- * See https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#isolated_world
104
- * @default "ISOLATED"
102
+ * List of target browsers to include this entrypoint in. Defaults to being included in all
103
+ * builds. Cannot be used with `exclude`. You must choose one of the two options.
104
+ *
105
+ * @default undefined
105
106
  */
106
- world?: 'ISOLATED';
107
+ include?: TargetBrowser[];
107
108
  /**
108
- * Main function executed when the content script is loaded.
109
+ * List of target browsers to exclude this entrypoint from. Cannot be used with `include`. You
110
+ * must choose one of the two options.
111
+ *
112
+ * @default undefined
109
113
  */
110
- main(ctx: ContentScriptContext): void | Promise<void>;
114
+ exclude?: TargetBrowser[];
111
115
  }
112
- interface ContentScriptMainWorldDefinition extends ContentScriptBaseDefinition {
113
- /**
114
- * See https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#isolated_world
115
- */
116
- world: 'MAIN';
116
+ interface BackgroundEntrypointOptions extends BaseEntrypointOptions {
117
+ persistent?: PerBrowserOption<boolean>;
117
118
  /**
118
- * Main function executed when the content script is loaded.
119
+ * Set to `"module"` to output the background entrypoint as ESM. ESM outputs can share chunks and
120
+ * reduce the overall size of the bundled extension.
121
+ *
122
+ * When `undefined`, the background is bundled individually into an IIFE format.
123
+ *
124
+ * @default undefined
119
125
  */
120
- main(): void | Promise<void>;
126
+ type?: PerBrowserOption<'module'>;
121
127
  }
122
- interface ContentScriptBaseDefinition extends ExcludableEntrypoint {
128
+ interface BaseContentScriptEntrypointOptions extends BaseEntrypointOptions {
123
129
  matches: PerBrowserOption<Manifest.ContentScript['matches']>;
124
130
  /**
125
131
  * See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
@@ -165,41 +171,68 @@ interface ContentScriptBaseDefinition extends ExcludableEntrypoint {
165
171
  * onto the page. Use `browser.runtime.getURL("content-scripts/<name>.css")` to get the file's
166
172
  * URL
167
173
  * - `"ui"` - Exclude the CSS from the manifest. CSS will be automatically added to your UI when
168
- * calling `createContentScriptUi`
174
+ * calling `createShadowRootUi`
169
175
  *
170
176
  * @default "manifest"
171
177
  */
172
178
  cssInjectionMode?: PerBrowserOption<'manifest' | 'manual' | 'ui'>;
179
+ /**
180
+ * Specify how the content script is registered.
181
+ *
182
+ * - `"manifest"`: The content script will be added to the `content_scripts` entry in the
183
+ * manifest. This is the normal and most well known way of registering a content script.
184
+ * - `"runtime"`: The content script's `matches` is added to `host_permissions` and you are
185
+ * responsible for using the scripting API to register the content script dynamically at
186
+ * runtime.
187
+ *
188
+ * @default "manifest"
189
+ */
190
+ registration?: PerBrowserOption<'manifest' | 'runtime'>;
173
191
  }
174
- interface BackgroundDefinition extends ExcludableEntrypoint {
175
- type?: PerBrowserOption<'module'>;
176
- persistent?: PerBrowserOption<boolean>;
177
- main(): void;
192
+ interface MainWorldContentScriptEntrypointOptions extends BaseContentScriptEntrypointOptions {
193
+ /**
194
+ * See https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#isolated_world
195
+ */
196
+ world: 'MAIN';
178
197
  }
179
- interface UnlistedScriptDefinition extends ExcludableEntrypoint {
198
+ interface IsolatedWorldContentScriptEntrypointOptions extends BaseContentScriptEntrypointOptions {
180
199
  /**
181
- * Main function executed when the unlisted script is ran.
200
+ * See https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#isolated_world
201
+ * @default "ISOLATED"
202
+ */
203
+ world?: 'ISOLATED';
204
+ }
205
+ interface IsolatedWorldContentScriptDefinition extends IsolatedWorldContentScriptEntrypointOptions {
206
+ /**
207
+ * Main function executed when the content script is loaded.
208
+ */
209
+ main(ctx: ContentScriptContext): void | Promise<void>;
210
+ }
211
+ interface MainWorldContentScriptDefinition extends MainWorldContentScriptEntrypointOptions {
212
+ /**
213
+ * Main function executed when the content script is loaded.
182
214
  */
183
215
  main(): void | Promise<void>;
184
216
  }
185
- type PerBrowserOption<T> = T | {
186
- [browser: TargetBrowser]: T;
187
- };
188
- interface ExcludableEntrypoint {
217
+ type ContentScriptDefinition = IsolatedWorldContentScriptDefinition | MainWorldContentScriptDefinition;
218
+ interface BackgroundDefinition extends BackgroundEntrypointOptions {
189
219
  /**
190
- * List of target browsers to include this entrypoint in. Defaults to being included in all
191
- * builds. Cannot be used with `exclude`. You must choose one of the two options.
192
- *
193
- * @default undefined
220
+ * Main function executed when the background script is started. Cannot be async.
194
221
  */
195
- include?: TargetBrowser[];
222
+ main(): void;
223
+ }
224
+ interface UnlistedScriptDefinition extends BaseEntrypointOptions {
196
225
  /**
197
- * List of target browsers to exclude this entrypoint from. Cannot be used with `include`. You
198
- * must choose one of the two options.
199
- *
200
- * @default undefined
226
+ * Main function executed when the unlisted script is ran.
201
227
  */
202
- exclude?: TargetBrowser[];
228
+ main(): void | Promise<void>;
203
229
  }
230
+ /**
231
+ * Either a single value or a map of different browsers to the value for that browser.
232
+ */
233
+ type PerBrowserOption<T> = T | PerBrowserMap<T>;
234
+ type PerBrowserMap<T> = {
235
+ [browser: TargetBrowser]: T;
236
+ };
204
237
 
205
238
  export { type BackgroundDefinition as B, type ContentScriptDefinition as C, type UnlistedScriptDefinition as U, ContentScriptContext as a };
package/dist/index.cjs CHANGED
@@ -2652,6 +2652,14 @@ function resolvePerBrowserOption(option, browser) {
2652
2652
  return option[browser];
2653
2653
  return option;
2654
2654
  }
2655
+ function resolvePerBrowserOptions(options, browser) {
2656
+ return Object.fromEntries(
2657
+ Object.entries(options).map(([key, value]) => [
2658
+ key,
2659
+ key === "defaultIcon" ? value : resolvePerBrowserOption(value, browser)
2660
+ ])
2661
+ );
2662
+ }
2655
2663
 
2656
2664
  // src/core/utils/constants.ts
2657
2665
  var VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
@@ -2690,6 +2698,8 @@ async function findEntrypoints() {
2690
2698
  switch (type) {
2691
2699
  case "popup":
2692
2700
  return await getPopupEntrypoint(info);
2701
+ case "sidepanel":
2702
+ return await getSidepanelEntrypoint(info);
2693
2703
  case "options":
2694
2704
  return await getOptionsEntrypoint(info);
2695
2705
  case "background":
@@ -2800,100 +2810,66 @@ function preventNoEntrypoints(files) {
2800
2810
  throw Error(`No entrypoints found in ${wxt.config.entrypointsDir}`);
2801
2811
  }
2802
2812
  }
2803
- function getHtmlBaseOptions(document) {
2804
- const options = {};
2805
- const includeContent = document.querySelector("meta[name='manifest.include']")?.getAttribute("content");
2806
- if (includeContent) {
2807
- options.include = import_json5.default.parse(includeContent);
2808
- }
2809
- const excludeContent = document.querySelector("meta[name='manifest.exclude']")?.getAttribute("content");
2810
- if (excludeContent) {
2811
- options.exclude = import_json5.default.parse(excludeContent);
2812
- }
2813
- return options;
2814
- }
2815
- async function getPopupEntrypoint({
2816
- inputPath,
2817
- name,
2818
- skipped
2819
- }) {
2820
- const content = await import_fs_extra3.default.readFile(inputPath, "utf-8");
2821
- const { document } = (0, import_linkedom.parseHTML)(content);
2822
- const options = getHtmlBaseOptions(document);
2823
- const title = document.querySelector("title");
2824
- if (title != null)
2825
- options.defaultTitle = title.textContent ?? void 0;
2826
- const defaultIconContent = document.querySelector("meta[name='manifest.default_icon']")?.getAttribute("content");
2827
- if (defaultIconContent) {
2828
- try {
2829
- options.defaultIcon = import_json5.default.parse(defaultIconContent);
2830
- } catch (err) {
2831
- wxt.logger.fatal(
2832
- `Failed to parse default_icon meta tag content as JSON5. content=${defaultIconContent}`,
2833
- err
2834
- );
2813
+ async function getPopupEntrypoint(info) {
2814
+ const options = await getHtmlEntrypointOptions(
2815
+ info,
2816
+ {
2817
+ browserStyle: "browse_style",
2818
+ exclude: "exclude",
2819
+ include: "include",
2820
+ defaultIcon: "default_icon",
2821
+ defaultTitle: "default_title",
2822
+ mv2Key: "type"
2823
+ },
2824
+ {
2825
+ defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
2826
+ },
2827
+ {
2828
+ defaultTitle: (content) => content,
2829
+ mv2Key: (content) => content === "page_action" ? "page_action" : "browser_action"
2835
2830
  }
2836
- }
2837
- const mv2TypeContent = document.querySelector("meta[name='manifest.type']")?.getAttribute("content");
2838
- if (mv2TypeContent) {
2839
- options.mv2Key = mv2TypeContent === "page_action" ? "page_action" : "browser_action";
2840
- }
2841
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
2842
- if (browserStyleContent) {
2843
- options.browserStyle = browserStyleContent === "true";
2844
- }
2831
+ );
2845
2832
  return {
2846
2833
  type: "popup",
2847
2834
  name: "popup",
2848
- options,
2849
- inputPath,
2835
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
2836
+ inputPath: info.inputPath,
2850
2837
  outputDir: wxt.config.outDir,
2851
- skipped
2838
+ skipped: info.skipped
2852
2839
  };
2853
2840
  }
2854
- async function getOptionsEntrypoint({
2855
- inputPath,
2856
- name,
2857
- skipped
2858
- }) {
2859
- const content = await import_fs_extra3.default.readFile(inputPath, "utf-8");
2860
- const { document } = (0, import_linkedom.parseHTML)(content);
2861
- const options = getHtmlBaseOptions(document);
2862
- const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
2863
- if (openInTabContent) {
2864
- options.openInTab = openInTabContent === "true";
2865
- }
2866
- const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
2867
- if (chromeStyleContent) {
2868
- options.chromeStyle = chromeStyleContent === "true";
2869
- }
2870
- const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
2871
- if (browserStyleContent) {
2872
- options.browserStyle = browserStyleContent === "true";
2873
- }
2841
+ async function getOptionsEntrypoint(info) {
2842
+ const options = await getHtmlEntrypointOptions(
2843
+ info,
2844
+ {
2845
+ browserStyle: "browse_style",
2846
+ chromeStyle: "chrome_style",
2847
+ exclude: "exclude",
2848
+ include: "include",
2849
+ openInTab: "open_in_tab"
2850
+ }
2851
+ );
2874
2852
  return {
2875
2853
  type: "options",
2876
2854
  name: "options",
2877
- options,
2878
- inputPath,
2855
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
2856
+ inputPath: info.inputPath,
2879
2857
  outputDir: wxt.config.outDir,
2880
- skipped
2858
+ skipped: info.skipped
2881
2859
  };
2882
2860
  }
2883
- async function getUnlistedPageEntrypoint({
2884
- inputPath,
2885
- name,
2886
- skipped
2887
- }) {
2888
- const content = await import_fs_extra3.default.readFile(inputPath, "utf-8");
2889
- const { document } = (0, import_linkedom.parseHTML)(content);
2861
+ async function getUnlistedPageEntrypoint(info) {
2862
+ const options = await getHtmlEntrypointOptions(info, {
2863
+ exclude: "exclude",
2864
+ include: "include"
2865
+ });
2890
2866
  return {
2891
2867
  type: "unlisted-page",
2892
- name: getEntrypointName(wxt.config.entrypointsDir, inputPath),
2893
- inputPath,
2868
+ name: info.name,
2869
+ inputPath: info.inputPath,
2894
2870
  outputDir: wxt.config.outDir,
2895
- options: getHtmlBaseOptions(document),
2896
- skipped
2871
+ options,
2872
+ skipped: info.skipped
2897
2873
  };
2898
2874
  }
2899
2875
  async function getUnlistedScriptEntrypoint({
@@ -2907,14 +2883,13 @@ async function getUnlistedScriptEntrypoint({
2907
2883
  `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
2908
2884
  );
2909
2885
  }
2910
- const { main: _, ...moduleOptions } = defaultExport;
2911
- const options = moduleOptions;
2886
+ const { main: _, ...options } = defaultExport;
2912
2887
  return {
2913
2888
  type: "unlisted-script",
2914
2889
  name,
2915
2890
  inputPath,
2916
2891
  outputDir: wxt.config.outDir,
2917
- options,
2892
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
2918
2893
  skipped
2919
2894
  };
2920
2895
  }
@@ -2942,14 +2917,7 @@ async function getBackgroundEntrypoint({
2942
2917
  name,
2943
2918
  inputPath,
2944
2919
  outputDir: wxt.config.outDir,
2945
- options: {
2946
- ...options,
2947
- type: resolvePerBrowserOption(options.type, wxt.config.browser),
2948
- persistent: resolvePerBrowserOption(
2949
- options.persistent,
2950
- wxt.config.browser
2951
- )
2952
- },
2920
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
2953
2921
  skipped
2954
2922
  };
2955
2923
  }
@@ -2969,10 +2937,58 @@ async function getContentScriptEntrypoint({
2969
2937
  name,
2970
2938
  inputPath,
2971
2939
  outputDir: (0, import_path2.resolve)(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
2972
- options,
2940
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
2973
2941
  skipped
2974
2942
  };
2975
2943
  }
2944
+ async function getSidepanelEntrypoint(info) {
2945
+ const options = await getHtmlEntrypointOptions(
2946
+ info,
2947
+ {
2948
+ browserStyle: "browse_style",
2949
+ exclude: "exclude",
2950
+ include: "include",
2951
+ defaultIcon: "default_icon",
2952
+ defaultTitle: "default_title",
2953
+ openAtInstall: "open_at_install"
2954
+ },
2955
+ {
2956
+ defaultTitle: (document) => document.querySelector("title")?.textContent || void 0
2957
+ },
2958
+ {
2959
+ defaultTitle: (content) => content
2960
+ }
2961
+ );
2962
+ return {
2963
+ type: "sidepanel",
2964
+ name: info.name,
2965
+ options: resolvePerBrowserOptions(options, wxt.config.browser),
2966
+ inputPath: info.inputPath,
2967
+ outputDir: wxt.config.outDir,
2968
+ skipped: info.skipped
2969
+ };
2970
+ }
2971
+ async function getHtmlEntrypointOptions(info, keyMap, queries, parsers) {
2972
+ const content = await import_fs_extra3.default.readFile(info.inputPath, "utf-8");
2973
+ const { document } = (0, import_linkedom.parseHTML)(content);
2974
+ const options = {};
2975
+ const defaultQuery = (manifestKey) => document.querySelector(`meta[name='manifest.${manifestKey}']`)?.getAttribute("content");
2976
+ Object.entries(keyMap).forEach(([_key, manifestKey]) => {
2977
+ const key = _key;
2978
+ const content2 = queries?.[key] ? queries[key](document, manifestKey) : defaultQuery(manifestKey);
2979
+ if (content2) {
2980
+ try {
2981
+ options[key] = (parsers?.[key] ?? import_json5.default.parse)(content2);
2982
+ } catch (err) {
2983
+ wxt.logger.fatal(
2984
+ `Failed to parse meta tag content. Usually this means you have invalid JSON5 content (content=${content2})`,
2985
+ err
2986
+ );
2987
+ }
2988
+ }
2989
+ });
2990
+ return options;
2991
+ }
2976
2992
  var PATH_GLOB_TO_TYPE_MAP = {
2977
2993
  "sandbox.html": "sandbox",
2978
2994
  "sandbox/index.html": "sandbox",
@@ -4081,6 +4097,7 @@ async function resolveConfig(inlineConfig, command, server) {
4081
4097
  let userConfigMetadata;
4082
4098
  if (inlineConfig.configFile !== false) {
4083
4099
  const { config: loadedConfig, ...metadata } = await (0, import_c12.loadConfig)({
4100
+ configFile: inlineConfig.configFile,
4084
4101
  name: "wxt",
4085
4102
  cwd: inlineConfig.root ?? process.cwd(),
4086
4103
  rcFile: false,
@@ -4460,7 +4477,12 @@ function getEsbuildOptions(opts) {
4460
4477
  return {
4461
4478
  format: "cjs",
4462
4479
  loader: isJsx ? "tsx" : "ts",
4463
- jsx: isJsx ? "automatic" : void 0
4480
+ ...isJsx ? {
4481
+ // `h` and `Fragment` are undefined, but that's OK because JSX is never evaluated while
4482
+ // grabbing the entrypoint's options.
4483
+ jsxFactory: "h",
4484
+ jsxFragment: "Fragment"
4485
+ } : void 0
4464
4486
  };
4465
4487
  }
4466
4488
 
@@ -4579,7 +4601,7 @@ function getChunkSortWeight(filename) {
4579
4601
  var import_picocolors4 = __toESM(require("picocolors"), 1);
4580
4602
 
4581
4603
  // package.json
4582
- var version = "0.16.8";
4604
+ var version = "0.16.10";
4583
4605
 
4584
4606
  // src/core/utils/log/printHeader.ts
4585
4607
  var import_consola2 = require("consola");
@@ -4636,7 +4658,11 @@ var ContentSecurityPolicy = class _ContentSecurityPolicy {
4636
4658
 
4637
4659
  // src/core/utils/content-scripts.ts
4638
4660
  function hashContentScriptOptions(options) {
4639
- const simplifiedOptions = mapWxtOptionsToContentScript(options);
4661
+ const simplifiedOptions = mapWxtOptionsToContentScript(
4662
+ options,
4663
+ void 0,
4664
+ void 0
4665
+ );
4640
4666
  Object.keys(simplifiedOptions).forEach((key) => {
4641
4667
  if (simplifiedOptions[key] == null)
4642
4668
  delete simplifiedOptions[key];
@@ -4662,32 +4688,31 @@ function hashContentScriptOptions(options) {
4662
4688
  }).sort((l, r) => l[0].localeCompare(r[0]))
4663
4689
  );
4664
4690
  }
4665
- function mapWxtOptionsToContentScript(options) {
4691
+ function mapWxtOptionsToContentScript(options, js, css) {
4666
4692
  return {
4667
- matches: resolvePerBrowserOption(options.matches, wxt.config.browser),
4668
- all_frames: resolvePerBrowserOption(options.allFrames, wxt.config.browser),
4669
- match_about_blank: resolvePerBrowserOption(
4670
- options.matchAboutBlank,
4671
- wxt.config.browser
4672
- ),
4673
- exclude_globs: resolvePerBrowserOption(
4674
- options.excludeGlobs,
4675
- wxt.config.browser
4676
- ),
4677
- exclude_matches: resolvePerBrowserOption(
4678
- options.excludeMatches,
4679
- wxt.config.browser
4680
- ),
4681
- include_globs: resolvePerBrowserOption(
4682
- options.includeGlobs,
4683
- wxt.config.browser
4684
- ),
4685
- run_at: resolvePerBrowserOption(options.runAt, wxt.config.browser),
4693
+ matches: options.matches,
4694
+ all_frames: options.allFrames,
4695
+ match_about_blank: options.matchAboutBlank,
4696
+ exclude_globs: options.excludeGlobs,
4697
+ exclude_matches: options.excludeMatches,
4698
+ include_globs: options.includeGlobs,
4699
+ run_at: options.runAt,
4700
+ css,
4701
+ js,
4686
4702
  // @ts-expect-error: untyped chrome options
4687
- match_origin_as_fallback: resolvePerBrowserOption(
4688
- options.matchOriginAsFallback,
4689
- wxt.config.browser
4690
- ),
4703
+ match_origin_as_fallback: options.matchOriginAsFallback,
4704
+ world: options.world
4705
+ };
4706
+ }
4707
+ function mapWxtOptionsToRegisteredContentScript(options, js, css) {
4708
+ return {
4709
+ allFrames: options.allFrames,
4710
+ excludeMatches: options.excludeMatches,
4711
+ matches: options.matches,
4712
+ runAt: options.runAt,
4713
+ js,
4714
+ css,
4715
+ // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
4691
4716
  world: options.world
4692
4717
  };
4693
4718
  }
@@ -4922,9 +4947,11 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
4922
4947
  );
4923
4948
  if (wxt.config.browser === "firefox") {
4924
4949
  manifest.sidebar_action = {
4925
- // TODO: Add options to side panel
4926
- // ...defaultSidepanel.options,
4927
- default_panel: page
4950
+ default_panel: page,
4951
+ browser_style: defaultSidepanel.options.browserStyle,
4952
+ default_icon: defaultSidepanel.options.defaultIcon,
4953
+ default_title: defaultSidepanel.options.defaultTitle,
4954
+ open_at_install: defaultSidepanel.options.openAtInstall
4928
4955
  };
4929
4956
  } else if (wxt.config.manifestVersion === 3) {
4930
4957
  manifest.side_panel = {
@@ -4939,21 +4966,13 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
4939
4966
  if (contentScripts?.length) {
4940
4967
  const cssMap = getContentScriptsCssMap(buildOutput, contentScripts);
4941
4968
  if (wxt.config.command === "serve" && wxt.config.manifestVersion === 3) {
4942
- const hostPermissions = new Set(manifest.host_permissions ?? []);
4943
4969
  contentScripts.forEach((script) => {
4944
- const matches = resolvePerBrowserOption(
4945
- script.options.matches,
4946
- wxt.config.browser
4947
- );
4948
- matches.forEach((matchPattern) => {
4949
- hostPermissions.add(matchPattern);
4970
+ script.options.matches.forEach((matchPattern) => {
4971
+ addHostPermission(manifest, matchPattern);
4950
4972
  });
4951
4973
  });
4952
- hostPermissions.forEach(
4953
- (permission) => addHostPermission(manifest, permission)
4954
- );
4955
4974
  } else {
4956
- const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
4975
+ const hashToEntrypointsMap = contentScripts.filter((cs) => cs.options.registration !== "runtime").reduce((map, script) => {
4957
4976
  const hash = hashContentScriptOptions(script.options);
4958
4977
  if (map.has(hash))
4959
4978
  map.get(hash)?.push(script);
@@ -4961,19 +4980,34 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
4961
4980
  map.set(hash, [script]);
4962
4981
  return map;
4963
4982
  }, /* @__PURE__ */ new Map());
4964
- const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
4965
- ([, scripts]) => ({
4966
- ...mapWxtOptionsToContentScript(scripts[0].options),
4967
- css: getContentScriptCssFiles(scripts, cssMap),
4968
- js: scripts.map(
4983
+ const manifestContentScripts = Array.from(
4984
+ hashToEntrypointsMap.values()
4985
+ ).map(
4986
+ (scripts) => mapWxtOptionsToContentScript(
4987
+ scripts[0].options,
4988
+ scripts.map(
4969
4989
  (entry) => getEntrypointBundlePath(entry, wxt.config.outDir, ".js")
4970
- )
4971
- })
4990
+ ),
4991
+ getContentScriptCssFiles(scripts, cssMap)
4992
+ )
4972
4993
  );
4973
- if (newContentScripts.length >= 0) {
4994
+ if (manifestContentScripts.length >= 0) {
4974
4995
  manifest.content_scripts ??= [];
4975
- manifest.content_scripts.push(...newContentScripts);
4996
+ manifest.content_scripts.push(...manifestContentScripts);
4976
4997
  }
4998
+ const runtimeContentScripts = contentScripts.filter(
4999
+ (cs) => cs.options.registration === "runtime"
5000
+ );
5001
+ if (runtimeContentScripts.length > 0 && wxt.config.manifestVersion === 2) {
5002
+ throw Error(
5003
+ 'Cannot use `registration: "runtime"` with MV2 content scripts, it is a MV3-only feature.'
5004
+ );
5005
+ }
5006
+ runtimeContentScripts.forEach((script) => {
5007
+ script.options.matches.forEach((matchPattern) => {
5008
+ addHostPermission(manifest, matchPattern);
5009
+ });
5010
+ });
4977
5011
  }
4978
5012
  const contentScriptCssResources = getContentScriptCssWebAccessibleResources(
4979
5013
  contentScripts,
@@ -5072,10 +5106,9 @@ function getContentScriptCssWebAccessibleResources(contentScripts, contentScript
5072
5106
  return;
5073
5107
  resources.push({
5074
5108
  resources: [cssFile],
5075
- matches: resolvePerBrowserOption(
5076
- script.options.matches,
5077
- wxt.config.browser
5078
- ).map((matchPattern) => stripPathFromMatchPattern(matchPattern))
5109
+ matches: script.options.matches.map(
5110
+ (matchPattern) => stripPathFromMatchPattern(matchPattern)
5111
+ )
5079
5112
  });
5080
5113
  });
5081
5114
  return resources;
@@ -5699,25 +5732,9 @@ function reloadContentScripts(steps, server) {
5699
5732
  const js = [getEntrypointBundlePath(entry, wxt.config.outDir, ".js")];
5700
5733
  const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
5701
5734
  const css = getContentScriptCssFiles([entry], cssMap);
5702
- server.reloadContentScript({
5703
- allFrames: resolvePerBrowserOption(
5704
- entry.options.allFrames,
5705
- wxt.config.browser
5706
- ),
5707
- excludeMatches: resolvePerBrowserOption(
5708
- entry.options.excludeMatches,
5709
- wxt.config.browser
5710
- ),
5711
- matches: resolvePerBrowserOption(
5712
- entry.options.matches,
5713
- wxt.config.browser
5714
- ),
5715
- runAt: resolvePerBrowserOption(entry.options.runAt, wxt.config.browser),
5716
- // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
5717
- world: resolvePerBrowserOption(entry.options.world, wxt.config.browser),
5718
- js,
5719
- css
5720
- });
5735
+ server.reloadContentScript(
5736
+ mapWxtOptionsToRegisteredContentScript(entry.options, js, css)
5737
+ );
5721
5738
  });
5722
5739
  } else {
5723
5740
  server.reloadExtension();
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as InlineConfig, B as BuildOutput, U as UserConfig, E as ExtensionRunnerConfig, W as WxtDevServer } from './index-sl22zA2Z.cjs';
2
- export { q as BackgroundDefinition, h as BackgroundEntrypoint, g as BaseEntrypoint, f as BaseEntrypointOptions, d as BuildStepOutput, w as ConfigEnv, p as ContentScriptBaseDefinition, m as ContentScriptDefinition, C as ContentScriptEntrypoint, n as ContentScriptIsolatedWorldDefinition, o as ContentScriptMainWorldDefinition, j as Entrypoint, k as EntrypointGroup, J as EslintGlobalsPropValue, K as Eslintrc, t as ExcludableEntrypoint, D as ExtensionRunner, F as FsCache, G as GenericEntrypoint, H as HookResult, L as Logger, l as OnContentScriptStopped, i as OptionsEntrypoint, c as OutputAsset, b as OutputChunk, O as OutputFile, s as PerBrowserOption, P as PopupEntrypoint, R as ResolvedConfig, M as ResolvedEslintrc, S as ServerInfo, T as TargetBrowser, e as TargetManifestVersion, r as UnlistedScriptDefinition, u as UserManifest, v as UserManifestFn, V as VirtualEntrypointType, A as Wxt, x as WxtBuilder, y as WxtBuilderServer, z as WxtHooks, Q as WxtResolvedUnimportOptions, N as WxtUnimportOptions, a as WxtViteConfig } from './index-sl22zA2Z.cjs';
1
+ import { I as InlineConfig, B as BuildOutput, U as UserConfig, E as ExtensionRunnerConfig, W as WxtDevServer } from './index-h54vKikt.cjs';
2
+ export { v as BackgroundDefinition, l as BackgroundEntrypoint, g as BackgroundEntrypointOptions, h as BaseContentScriptEntrypointOptions, k as BaseEntrypoint, f as BaseEntrypointOptions, d as BuildStepOutput, D as ConfigEnv, u as ContentScriptDefinition, C as ContentScriptEntrypoint, p as Entrypoint, q as EntrypointGroup, _ as EslintGlobalsPropValue, $ as Eslintrc, Y as ExtensionRunner, X as FsCache, G as GenericEntrypoint, K as HookResult, s as IsolatedWorldContentScriptDefinition, i as IsolatedWorldContentScriptEntrypointOptions, L as Logger, t as MainWorldContentScriptDefinition, M as MainWorldContentScriptEntrypointOptions, r as OnContentScriptStopped, n as OptionsEntrypoint, j as OptionsEntrypointOptions, c as OutputAsset, b as OutputChunk, O as OutputFile, y as PerBrowserMap, x as PerBrowserOption, m as PopupEntrypoint, P as PopupEntrypointOptions, V as ResolvedConfig, a0 as ResolvedEslintrc, R as ResolvedPerBrowserOptions, J as ServerInfo, o as SidepanelEntrypoint, S as SidepanelEntrypointOptions, T as TargetBrowser, e as TargetManifestVersion, w as UnlistedScriptDefinition, z as UserManifest, A as UserManifestFn, Z as VirtualEntrypointType, Q as Wxt, F as WxtBuilder, H as WxtBuilderServer, N as WxtHooks, a2 as WxtResolvedUnimportOptions, a1 as WxtUnimportOptions, a as WxtViteConfig } from './index-h54vKikt.cjs';
3
3
  import 'vite';
4
4
  import 'webextension-polyfill';
5
5
  import 'unimport';
@@ -64,6 +64,6 @@ declare function prepare(config: InlineConfig): Promise<void>;
64
64
  */
65
65
  declare function zip(config?: InlineConfig): Promise<string[]>;
66
66
 
67
- var version = "0.16.8";
67
+ var version = "0.16.10";
68
68
 
69
69
  export { BuildOutput, ExtensionRunnerConfig, InlineConfig, UserConfig, WxtDevServer, build, clean, createServer, defineConfig, defineRunnerConfig, initialize, prepare, version, zip };