wxt 0.20.25 → 0.20.26

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.
@@ -44,7 +44,7 @@ const aliasCommandNames = /* @__PURE__ */ new Set();
44
44
  * @param docsUrl URL to the docs for the aliased CLI tool
45
45
  */
46
46
  function createAliasedCommand(base, name, alias, bin, docsUrl) {
47
- const aliasedCommand = base.command(name, `Alias for ${alias} (${docsUrl})`).allowUnknownOptions().action(async () => {
47
+ const aliasedCommand = base.command(`${name} [...args]`, `Alias for ${alias} (${docsUrl})`).allowUnknownOptions().action(async () => {
48
48
  try {
49
49
  await registerWxt("build");
50
50
  await spawn(bin, process.argv.slice(process.argv.indexOf(aliasedCommand.name) + 1), { stdio: "inherit" });
@@ -1,21 +1,12 @@
1
1
  import { unnormalizePath } from "./utils/paths.mjs";
2
- import { getEntrypointBundlePath, isHtmlEntrypoint } from "./utils/entrypoints.mjs";
3
2
  import { deinitWxtModules, initWxtModules, registerWxt, wxt } from "./wxt.mjs";
4
- import { detectDevChanges } from "./utils/building/detect-dev-changes.mjs";
5
- import { findEntrypoints } from "./utils/building/find-entrypoints.mjs";
6
- import { getContentScriptJs, mapWxtOptionsToRegisteredContentScript } from "./utils/content-scripts.mjs";
7
- import { getContentScriptCssFiles, getContentScriptsCssMap } from "./utils/manifest.mjs";
8
- import { rebuild } from "./utils/building/rebuild.mjs";
9
3
  import { internalBuild } from "./utils/building/internal-build.mjs";
10
4
  import "./utils/building/index.mjs";
11
5
  import { createExtensionRunner } from "./runners/index.mjs";
12
6
  import { createKeyboardShortcuts } from "./keyboard-shortcuts.mjs";
13
7
  import { isBabelSyntaxError, logBabelSyntaxError } from "./utils/syntax-errors.mjs";
14
- import { relative } from "node:path";
15
- import { styleText } from "node:util";
16
- import { debounce } from "perfect-debounce";
8
+ import { createFileReloader, reloadContentScripts } from "./utils/create-file-reloader.mjs";
17
9
  import chokidar from "chokidar";
18
- import { Mutex } from "async-mutex";
19
10
  //#region src/core/create-server.ts
20
11
  /**
21
12
  * Creates a dev server and pre-builds all the files that need to exist before
@@ -147,93 +138,6 @@ async function createServerInternal() {
147
138
  return server;
148
139
  }
149
140
  /**
150
- * Returns a function responsible for reloading different parts of the extension
151
- * when a file changes.
152
- */
153
- function createFileReloader(server) {
154
- const fileChangedMutex = new Mutex();
155
- const changeQueue = [];
156
- const cb = async (event, path) => {
157
- changeQueue.push([event, path]);
158
- await fileChangedMutex.runExclusive(async () => {
159
- if (server.currentOutput == null) return;
160
- const fileChanges = changeQueue.splice(0, changeQueue.length).map(([_, file]) => file);
161
- if (fileChanges.length === 0) return;
162
- await wxt.reloadConfig();
163
- const changes = detectDevChanges(fileChanges, server.currentOutput);
164
- if (changes.type === "no-change") return;
165
- if (changes.type === "full-restart") {
166
- wxt.logger.info("Config changed, restarting server...");
167
- server.restart();
168
- return;
169
- }
170
- if (changes.type === "browser-restart") {
171
- wxt.logger.info("Runner config changed, restarting browser...");
172
- server.restartBrowser();
173
- return;
174
- }
175
- wxt.logger.info(`Changed: ${Array.from(new Set(fileChanges)).map((file) => styleText("dim", relative(wxt.config.root, file))).join(", ")}`);
176
- const allEntrypoints = await findEntrypoints();
177
- try {
178
- const { output: newOutput } = await rebuild(allEntrypoints, changes.rebuildGroups, changes.cachedOutput);
179
- server.currentOutput = newOutput;
180
- switch (changes.type) {
181
- case "extension-reload":
182
- server.reloadExtension();
183
- wxt.logger.success(`Reloaded extension`);
184
- break;
185
- case "html-reload":
186
- const { reloadedNames } = reloadHtmlPages(changes.rebuildGroups, server);
187
- wxt.logger.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
188
- break;
189
- case "content-script-reload":
190
- reloadContentScripts(changes.changedSteps, server);
191
- const rebuiltNames = changes.rebuildGroups.flat().map((entry) => entry.name);
192
- wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
193
- break;
194
- }
195
- } catch {}
196
- }).catch((error) => {
197
- if (!isBabelSyntaxError(error)) throw error;
198
- logBabelSyntaxError(error);
199
- });
200
- };
201
- return debounce(cb, wxt.config.dev.server.watchDebounce, {
202
- leading: true,
203
- trailing: false
204
- });
205
- }
206
- /**
207
- * From the server, tell the client to reload content scripts from the provided
208
- * build step outputs.
209
- */
210
- function reloadContentScripts(steps, server) {
211
- if (wxt.config.manifestVersion === 3) steps.forEach((step) => {
212
- if (server.currentOutput == null) return;
213
- const entry = step.entrypoints;
214
- if (Array.isArray(entry) || entry.type !== "content-script") return;
215
- const js = getContentScriptJs(wxt.config, entry);
216
- const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
217
- const css = getContentScriptCssFiles([entry], cssMap);
218
- server.reloadContentScript({
219
- registration: entry.options.registration,
220
- contentScript: mapWxtOptionsToRegisteredContentScript(entry.options, js, css)
221
- });
222
- });
223
- else server.reloadExtension();
224
- }
225
- function reloadHtmlPages(groups, server) {
226
- const htmlEntries = groups.flat().filter(isHtmlEntrypoint);
227
- htmlEntries.forEach((entry) => {
228
- const path = getEntrypointBundlePath(entry, wxt.config.outDir, ".html");
229
- server.reloadPage(path);
230
- });
231
- return { reloadedNames: htmlEntries.map((entry) => entry.name) };
232
- }
233
- function getFilenameList(names) {
234
- return names.map((name) => styleText("cyan", name)).join(styleText("dim", ", "));
235
- }
236
- /**
237
141
  * Based on the current build output, return a list of files that are:
238
142
  *
239
143
  * 1. Not in node_modules
@@ -0,0 +1,103 @@
1
+ import { getEntrypointBundlePath, isHtmlEntrypoint } from "./entrypoints.mjs";
2
+ import { wxt } from "../wxt.mjs";
3
+ import { detectDevChanges } from "./building/detect-dev-changes.mjs";
4
+ import { findEntrypoints } from "./building/find-entrypoints.mjs";
5
+ import { getContentScriptJs, mapWxtOptionsToRegisteredContentScript } from "./content-scripts.mjs";
6
+ import { getContentScriptCssFiles, getContentScriptsCssMap } from "./manifest.mjs";
7
+ import { rebuild } from "./building/rebuild.mjs";
8
+ import "./building/index.mjs";
9
+ import { isBabelSyntaxError, logBabelSyntaxError } from "./syntax-errors.mjs";
10
+ import { relative } from "node:path";
11
+ import { styleText } from "node:util";
12
+ import { debounce } from "perfect-debounce";
13
+ import { Mutex } from "async-mutex";
14
+ //#region src/core/utils/create-file-reloader.ts
15
+ /**
16
+ * Returns a function responsible for reloading different parts of the extension
17
+ * when a file changes.
18
+ */
19
+ function createFileReloader(server) {
20
+ const fileChangedMutex = new Mutex();
21
+ const changeQueue = [];
22
+ const cb = async (event, path) => {
23
+ changeQueue.push([event, path]);
24
+ await fileChangedMutex.runExclusive(async () => {
25
+ if (server.currentOutput == null) return;
26
+ const fileChanges = changeQueue.splice(0, changeQueue.length).map(([_, file]) => file);
27
+ if (fileChanges.length === 0) return;
28
+ await wxt.reloadConfig();
29
+ const changes = detectDevChanges(fileChanges, server.currentOutput);
30
+ if (changes.type === "no-change") return;
31
+ if (changes.type === "full-restart") {
32
+ wxt.logger.info("Config changed, restarting server...");
33
+ server.restart();
34
+ return;
35
+ }
36
+ if (changes.type === "browser-restart") {
37
+ wxt.logger.info("Runner config changed, restarting browser...");
38
+ server.restartBrowser();
39
+ return;
40
+ }
41
+ wxt.logger.info(`Changed: ${Array.from(new Set(fileChanges)).map((file) => styleText("dim", relative(wxt.config.root, file))).join(", ")}`);
42
+ const allEntrypoints = await findEntrypoints();
43
+ try {
44
+ const { output: newOutput } = await rebuild(allEntrypoints, changes.rebuildGroups, changes.cachedOutput);
45
+ server.currentOutput = newOutput;
46
+ switch (changes.type) {
47
+ case "extension-reload":
48
+ server.reloadExtension();
49
+ wxt.logger.success(`Reloaded extension`);
50
+ break;
51
+ case "html-reload":
52
+ const { reloadedNames } = reloadHtmlPages(changes.rebuildGroups, server);
53
+ wxt.logger.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
54
+ break;
55
+ case "content-script-reload":
56
+ reloadContentScripts(changes.changedSteps, server);
57
+ const rebuiltNames = changes.rebuildGroups.flat().map((entry) => entry.name);
58
+ wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
59
+ break;
60
+ }
61
+ } catch {}
62
+ }).catch((error) => {
63
+ if (!isBabelSyntaxError(error)) throw error;
64
+ logBabelSyntaxError(error);
65
+ });
66
+ };
67
+ return debounce(cb, wxt.config.dev.server.watchDebounce, {
68
+ leading: true,
69
+ trailing: false
70
+ });
71
+ }
72
+ /**
73
+ * From the server, tell the client to reload content scripts from the provided
74
+ * build step outputs.
75
+ */
76
+ function reloadContentScripts(steps, server) {
77
+ if (wxt.config.manifestVersion === 3) steps.forEach((step) => {
78
+ if (server.currentOutput == null) return;
79
+ const entry = step.entrypoints;
80
+ if (Array.isArray(entry) || entry.type !== "content-script") return;
81
+ const js = getContentScriptJs(wxt.config, entry);
82
+ const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
83
+ const css = getContentScriptCssFiles([entry], cssMap);
84
+ server.reloadContentScript({
85
+ registration: entry.options.registration,
86
+ contentScript: mapWxtOptionsToRegisteredContentScript(entry.options, js, css)
87
+ });
88
+ });
89
+ else server.reloadExtension();
90
+ }
91
+ function reloadHtmlPages(groups, server) {
92
+ const htmlEntries = groups.flat().filter(isHtmlEntrypoint);
93
+ htmlEntries.forEach((entry) => {
94
+ const path = getEntrypointBundlePath(entry, wxt.config.outDir, ".html");
95
+ server.reloadPage(path);
96
+ });
97
+ return { reloadedNames: htmlEntries.map((entry) => entry.name) };
98
+ }
99
+ function getFilenameList(names) {
100
+ return names.map((name) => styleText("cyan", name)).join(styleText("dim", ", "));
101
+ }
102
+ //#endregion
103
+ export { createFileReloader, reloadContentScripts };
@@ -1,3 +1,4 @@
1
+ import { wxt } from "../../wxt.mjs";
1
2
  import { printTable } from "./printTable.mjs";
2
3
  import { lstat } from "node:fs/promises";
3
4
  import path from "node:path";
@@ -10,9 +11,14 @@ async function printFileList(log, header, baseDir, files) {
10
11
  const parts = [path.relative(process.cwd(), baseDir) + path.sep, path.relative(baseDir, file)];
11
12
  const prefix = i === files.length - 1 ? " └─" : " ├─";
12
13
  const chunkColor = getChunkColor(file);
13
- const stats = await lstat(file);
14
- totalSize += stats.size;
15
- const size = String(filesize(stats.size));
14
+ let size = "";
15
+ try {
16
+ const stats = await lstat(file);
17
+ totalSize += stats.size;
18
+ size = String(filesize(stats.size));
19
+ } catch (ex) {
20
+ wxt.logger.warn(`Could not get stats of '${file}' error: ${ex}`);
21
+ }
16
22
  return [`${styleText("gray", prefix)} ${styleText("dim", parts[0])}${styleText(chunkColor, parts[1])}`, styleText("dim", size)];
17
23
  }));
18
24
  fileRows.push([`${styleText("cyan", "Σ Total size:")} ${String(filesize(totalSize))}`]);
@@ -136,10 +136,11 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
136
136
  const options = {};
137
137
  if (popup.options.defaultIcon) options.default_icon = popup.options.defaultIcon;
138
138
  if (popup.options.defaultTitle) options.default_title = popup.options.defaultTitle;
139
+ if (popup.options.defaultState && wxt.config.manifestVersion === 3) options.default_state = popup.options.defaultState;
139
140
  if (popup.options.browserStyle) options.browser_style = popup.options.browserStyle;
140
141
  if (popup.options.defaultArea) options.default_area = popup.options.defaultArea;
141
142
  if (popup.options.themeIcons) options.theme_icons = popup.options.themeIcons;
142
- const actionKey = manifest.manifest_version === 2 ? popup.options.actionType ?? "browser_action" : wxt.config.browser === "firefox" && popup.options.actionType === "page_action" ? "page_action" : "action";
143
+ const actionKey = wxt.config.manifestVersion === 2 ? popup.options.actionType ?? "browser_action" : wxt.config.browser === "firefox" && popup.options.actionType === "page_action" ? "page_action" : "action";
143
144
  manifest[actionKey] = {
144
145
  ...manifest[actionKey],
145
146
  ...options,
@@ -150,7 +151,7 @@ function addEntrypoints(manifest, entrypoints, buildOutput) {
150
151
  if (options) {
151
152
  const page = getEntrypointBundlePath(options, wxt.config.outDir, ".html");
152
153
  manifest.options_ui = {
153
- open_in_tab: options.options.openInTab ?? false,
154
+ ...wxt.config.browser !== "safari" && { open_in_tab: options.options.openInTab ?? false },
154
155
  browser_style: wxt.config.browser === "firefox" ? options.options.browserStyle : void 0,
155
156
  chrome_style: wxt.config.browser !== "firefox" ? options.options.chromeStyle : void 0,
156
157
  page
@@ -239,9 +240,9 @@ function addDevModeCsp(manifest) {
239
240
  }
240
241
  const permission = `${permissionUrl}*`;
241
242
  const allowedCsp = wxt.server?.origin ?? "http://localhost:*";
242
- if (manifest.manifest_version === 3) addHostPermission(manifest, permission);
243
+ if (wxt.config.manifestVersion === 3) addHostPermission(manifest, permission);
243
244
  else addPermission(manifest, permission);
244
- const extensionPagesCsp = new ContentSecurityPolicy(manifest.content_security_policy?.extension_pages ?? (manifest.manifest_version === 3 ? DEFAULT_MV3_EXTENSION_PAGES_CSP : DEFAULT_MV2_CSP));
245
+ const extensionPagesCsp = new ContentSecurityPolicy(manifest.content_security_policy?.extension_pages ?? (wxt.config.manifestVersion === 3 ? DEFAULT_MV3_EXTENSION_PAGES_CSP : DEFAULT_MV2_CSP));
245
246
  const sandboxCsp = new ContentSecurityPolicy(manifest.content_security_policy?.sandbox ?? DEFAULT_MV3_SANDBOX_CSP);
246
247
  if (wxt.config.command === "serve") {
247
248
  extensionPagesCsp.add("script-src", allowedCsp);
@@ -8,10 +8,8 @@ import picomatch from "picomatch";
8
8
  * matches any of the negative patterns, it will return `false`.
9
9
  *
10
10
  * @example
11
- * ```ts
12
11
  * picomatchMultiple('a.json', ['*.json', '!b.json']); // => true
13
12
  * picomatchMultiple('b.json', ['*.json', '!b.json']); // => false
14
- * ```;
15
13
  */
16
14
  function picomatchMultiple(search, patterns, options) {
17
15
  if (patterns == null) return false;
package/dist/types.d.mts CHANGED
@@ -89,7 +89,7 @@ interface InlineConfig {
89
89
  * 'serve')
90
90
  *
91
91
  * @example
92
- * {{browser}} -mv{{manifestVersion}}
92
+ * '{{browser}}-mv{{manifestVersion}}';
93
93
  *
94
94
  * @default <span v-pre>`"{{browser}}-mv{{manifestVersion}}{{modeSuffix}}"`</span>
95
95
  */
@@ -168,13 +168,11 @@ interface InlineConfig {
168
168
  * Suppress specific warnings during the build process.
169
169
  *
170
170
  * @example
171
- * ```ts
172
171
  * export default defineConfig({
173
172
  * suppressWarnings: {
174
173
  * firefoxDataCollection: true,
175
174
  * },
176
- * })
177
- * ```;
175
+ * });
178
176
  */
179
177
  suppressWarnings?: {
180
178
  /**
@@ -309,10 +307,10 @@ interface InlineConfig {
309
307
  *
310
308
  * @example
311
309
  * // Correct:
312
- * ['@scope/package-name', 'package-name'][
313
- * // Incorrect, don't include versions!!!
314
- * ('@scope/package-name@1.1.3', 'package-name@^2')
315
- * ];
310
+ * ['@scope/package-name', 'package-name'];
311
+ *
312
+ * // Incorrect, don't include versions!!!
313
+ * ['@scope/package-name@1.1.3', 'package-name@^2'];
316
314
  *
317
315
  * @default [ ]
318
316
  */
@@ -699,6 +697,22 @@ interface BaseContentScriptEntrypointOptions extends BaseScriptEntrypointOptions
699
697
  * @default 'manifest'
700
698
  */
701
699
  registration?: PerBrowserOption<'manifest' | 'runtime'>;
700
+ /**
701
+ * Do not send the `wxt:content-script-started` message via
702
+ * `window.postMessage`.
703
+ *
704
+ * This has been replaced with custom events. The `postMessage` call is kept
705
+ * for backwards compatibility. For some websites the `postMessage` call is
706
+ * undesirable, such as those with poorly written message event listeners.
707
+ *
708
+ * Setting this to `true` opts into the behavior that will become the default
709
+ * in a future version of WXT, where the `postMessage` call is removed
710
+ * entirely.
711
+ *
712
+ * See https://github.com/wxt-dev/wxt/pull/1938 and
713
+ * https://github.com/wxt-dev/wxt/pull/2035 for a detailed discussion.
714
+ */
715
+ noScriptStartedPostMessage?: boolean;
702
716
  }
703
717
  interface MainWorldContentScriptEntrypointOptions extends BaseContentScriptEntrypointOptions {
704
718
  /**
@@ -742,6 +756,12 @@ interface PopupEntrypointOptions extends BaseEntrypointOptions {
742
756
  mv2Key?: PerBrowserOption<'browser_action' | 'page_action'>;
743
757
  defaultIcon?: Record<string, string>;
744
758
  defaultTitle?: PerBrowserOption<string>;
759
+ /**
760
+ * Chrome only. Controls the initial enabled/disabled state of the action.
761
+ *
762
+ * @see https://developer.chrome.com/docs/extensions/reference/api/action#enabled_state
763
+ */
764
+ defaultState?: PerBrowserOption<'enabled' | 'disabled'>;
745
765
  browserStyle?: PerBrowserOption<boolean>;
746
766
  /**
747
767
  * Firefox only. Defines the part of the browser in which the button is
@@ -169,7 +169,7 @@ var ContentScriptContext = class ContentScriptContext {
169
169
  contentScriptName: this.contentScriptName,
170
170
  messageId: this.id
171
171
  } }));
172
- window.postMessage({
172
+ if (!this.options?.noScriptStartedPostMessage) window.postMessage({
173
173
  type: ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE,
174
174
  contentScriptName: this.contentScriptName,
175
175
  messageId: this.id
package/dist/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region src/version.ts
2
- const version = "0.20.25";
2
+ const version = "0.20.26";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wxt",
3
3
  "type": "module",
4
- "version": "0.20.25",
4
+ "version": "0.20.26",
5
5
  "description": "⚡ Next-gen Web Extension Framework",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -21,7 +21,7 @@
21
21
  "@webext-core/fake-browser": "^1.3.4",
22
22
  "@webext-core/isolated-element": "^1.1.3",
23
23
  "@webext-core/match-patterns": "^1.0.3",
24
- "@wxt-dev/browser": "^0.1.40",
24
+ "@wxt-dev/browser": "^0.1.42",
25
25
  "@wxt-dev/storage": "^1.0.0",
26
26
  "async-mutex": "^0.5.0",
27
27
  "c12": "^3.3.3",
@@ -51,7 +51,7 @@
51
51
  "perfect-debounce": "^2.1.0",
52
52
  "picomatch": "^4.0.3",
53
53
  "prompts": "^2.4.2",
54
- "publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.4",
54
+ "publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.5",
55
55
  "scule": "^1.3.0",
56
56
  "tinyglobby": "^0.2.15",
57
57
  "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0 || ^6.0.0",
@@ -71,7 +71,6 @@
71
71
  "@aklinker1/buildc": "^1.1.7",
72
72
  "@faker-js/faker": "^10.3.0",
73
73
  "@types/bun": "^1.3.5",
74
- "@types/fs-extra": "^11.0.4",
75
74
  "@types/lodash.merge": "^4.6.9",
76
75
  "@types/node": "^20.17.6",
77
76
  "@types/normalize-path": "^3.0.2",
@@ -81,11 +80,11 @@
81
80
  "extract-zip": "^2.0.1",
82
81
  "happy-dom": "^20.8.3",
83
82
  "lodash.merge": "^4.6.2",
84
- "oxlint": "^1.51.0",
83
+ "oxlint": "^1.63.0",
85
84
  "publint": "^0.3.18",
86
85
  "tsdown": "^0.21.0",
87
- "typescript": "^5.9.3",
88
- "vitest": "^4.0.18",
86
+ "typescript": "^6.0.3",
87
+ "vitest": "^4.1.5",
89
88
  "vitest-plugin-random-seed": "^1.1.2"
90
89
  },
91
90
  "repository": {