wxt 0.2.2 → 0.2.4

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/index.d.ts CHANGED
@@ -91,6 +91,57 @@ interface InlineConfig {
91
91
  * Custom runner options. Options set here can be overridden in a `web-ext.config.ts` file.
92
92
  */
93
93
  runner?: ExtensionRunnerConfig;
94
+ zip?: {
95
+ /**
96
+ * Configure the filename output when zipping files.
97
+ *
98
+ * Available template variables:
99
+ *
100
+ * - `{{name}}` - The project's name converted to kebab-case
101
+ * - `{{version}} - The version_name or version from the manifest
102
+ * - `{{browser}} - The target browser from the `--browser` CLI flag
103
+ * - `{{manifestVersion}}` - Either "2" or "3"
104
+ *
105
+ * @default "{{name}}-{{version}}-{{browser}}.zip"
106
+ */
107
+ artifactTemplate?: string;
108
+ /**
109
+ * Configure the filename output when zipping files.
110
+ *
111
+ * Available template variables:
112
+ *
113
+ * - `{{name}}` - The project's name converted to kebab-case
114
+ * - `{{version}} - The version_name or version from the manifest
115
+ * - `{{browser}} - The target browser from the `--browser` CLI flag
116
+ * - `{{manifestVersion}}` - Either "2" or "3"
117
+ *
118
+ * @default "{{name}}-{{version}}-sources.zip"
119
+ */
120
+ sourcesTemplate?: string;
121
+ /**
122
+ * Override the artifactTemplate's `{{name}}` template variable. Defaults to the package.json's
123
+ * name, or if that doesn't exist, the current working directories name.
124
+ */
125
+ name?: string;
126
+ /**
127
+ * Root directory to ZIP. The ZIP can be uploaded to the Firefox Addon Store as your source
128
+ * code. Defaults to the `config.root` directory.
129
+ */
130
+ sourcesRoot?: string;
131
+ /**
132
+ * [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to exclude when
133
+ * creating a ZIP of all your source code for Firfox. Patterns are relative to your
134
+ * `config.zip.sourcesRoot`.
135
+ *
136
+ * Hidden files, node_modules, and tests are ignored by default.
137
+ *
138
+ * @example
139
+ * [
140
+ * "coverage", // Ignore the coverage directory in the `sourcesRoot`
141
+ * ]
142
+ */
143
+ ignoredSources?: string[];
144
+ };
94
145
  }
95
146
  interface WxtInlineViteConfig extends Omit<vite.InlineConfig, 'root' | 'configFile' | 'mode' | 'build'> {
96
147
  build?: Omit<vite.BuildOptions, 'outDir'>;
@@ -275,10 +326,10 @@ interface BackgroundScriptDefintition {
275
326
  main(): void;
276
327
  }
277
328
  /**
278
- * Manifest customization available in the `wxt.config.ts` file. Any missing fields like "name"
279
- * and "version" are managed automatically, and don't need to be listed here.
329
+ * Manifest customization available in the `wxt.config.ts` file. You cannot configure entrypoints
330
+ * here, they are configured inline.
280
331
  */
281
- type UserManifest = Omit<Manifest.WebExtensionManifest, 'action' | 'background' | 'browser_action' | 'chrome_url_overrides' | 'content_scripts' | 'description' | 'devtools_page' | 'manifest_version' | 'name' | 'options_page' | 'options_ui' | 'sandbox' | 'page_action' | 'popup' | 'short_name' | 'sidepanel' | 'sidebar_action' | 'version' | 'version_name'>;
332
+ type UserManifest = Partial<Omit<Manifest.WebExtensionManifest, 'action' | 'background' | 'browser_action' | 'chrome_url_overrides' | 'content_scripts' | 'devtools_page' | 'manifest_version' | 'options_page' | 'options_ui' | 'sandbox' | 'page_action' | 'popup' | 'sidepanel' | 'sidebar_action'>>;
282
333
  type UserManifestFn = (env: ConfigEnv) => UserManifest | Promise<UserManifest>;
283
334
  interface ConfigEnv {
284
335
  mode: string;
@@ -353,7 +404,7 @@ interface ExtensionRunnerConfig {
353
404
 
354
405
  type EntrypointGroup = Entrypoint | Entrypoint[];
355
406
 
356
- var version = "0.2.2";
407
+ var version = "0.2.4";
357
408
 
358
409
  declare function defineConfig(config: UserConfig): UserConfig;
359
410
 
package/dist/index.js CHANGED
@@ -430,7 +430,27 @@ async function getInternalConfig(config, command) {
430
430
  wxtDir,
431
431
  typesDir,
432
432
  fsCache: createFsCache(wxtDir),
433
- manifest
433
+ manifest,
434
+ zip: {
435
+ sourcesTemplate: "{{name}}-{{version}}-sources.zip",
436
+ artifactTemplate: "{{name}}-{{version}}-{{browser}}.zip",
437
+ sourcesRoot: root,
438
+ ...userConfig.zip,
439
+ ...config.zip,
440
+ ignoredSources: [
441
+ "**/node_modules",
442
+ // WXT files
443
+ "**/web-ext.config.ts",
444
+ // Hidden files
445
+ "**/.*",
446
+ // Tests
447
+ "**/__tests__/**",
448
+ "**/*.+(test|spec).?(c|m)+(j|t)s?(x)",
449
+ // User config
450
+ ...userConfig.zip?.ignoredSources ?? [],
451
+ ...config.zip?.ignoredSources ?? []
452
+ ]
453
+ }
434
454
  };
435
455
  finalConfig.vite.root = root;
436
456
  finalConfig.vite.configFile = false;
@@ -558,7 +578,7 @@ function findEffectedSteps(changedFile, currentOutput) {
558
578
  // src/index.ts
559
579
  import { Mutex } from "async-mutex";
560
580
  import { consola as consola2 } from "consola";
561
- import { relative as relative6 } from "node:path";
581
+ import { relative as relative5 } from "node:path";
562
582
 
563
583
  // src/core/build/buildEntrypoints.ts
564
584
  import * as vite2 from "vite";
@@ -850,15 +870,15 @@ async function getOptionsEntrypoint(config, path5) {
850
870
  const { document } = parseHTML2(content);
851
871
  const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
852
872
  if (openInTabContent) {
853
- options.openInTab = Boolean(openInTabContent);
873
+ options.openInTab = openInTabContent === "true";
854
874
  }
855
875
  const chromeStyleContent = document.querySelector("meta[name='manifest.chrome_style']")?.getAttribute("content");
856
876
  if (chromeStyleContent) {
857
- options.chromeStyle = Boolean(chromeStyleContent);
877
+ options.chromeStyle = chromeStyleContent === "true";
858
878
  }
859
879
  const browserStyleContent = document.querySelector("meta[name='manifest.browser_style']")?.getAttribute("content");
860
880
  if (browserStyleContent) {
861
- options.browserStyle = Boolean(browserStyleContent);
881
+ options.browserStyle = browserStyleContent === "true";
862
882
  }
863
883
  return {
864
884
  type: "options",
@@ -1049,8 +1069,8 @@ async function writeTsConfigFile(mainReference, config) {
1049
1069
  }
1050
1070
 
1051
1071
  // src/core/utils/manifest.ts
1052
- import fs9 from "fs-extra";
1053
- import { resolve as resolve11 } from "path";
1072
+ import fs10 from "fs-extra";
1073
+ import { resolve as resolve12 } from "path";
1054
1074
 
1055
1075
  // src/core/utils/ContentSecurityPolicy.ts
1056
1076
  var ContentSecurityPolicy = class _ContentSecurityPolicy {
@@ -1133,11 +1153,26 @@ function mapWxtOptionsToContentScript(options) {
1133
1153
  };
1134
1154
  }
1135
1155
 
1156
+ // src/core/utils/package.ts
1157
+ import { resolve as resolve11 } from "node:path";
1158
+ import fs9 from "fs-extra";
1159
+ async function getPackageJson(config) {
1160
+ const file = resolve11(config.root, "package.json");
1161
+ try {
1162
+ return await fs9.readJson(file);
1163
+ } catch (err) {
1164
+ config.logger.debug(
1165
+ `Failed to read package.json at: ${file}. Returning undefined.`
1166
+ );
1167
+ return {};
1168
+ }
1169
+ }
1170
+
1136
1171
  // src/core/utils/manifest.ts
1137
1172
  async function writeManifest(manifest, output, config) {
1138
1173
  const str = config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
1139
- await fs9.ensureDir(config.outDir);
1140
- await fs9.writeFile(resolve11(config.outDir, "manifest.json"), str, "utf-8");
1174
+ await fs10.ensureDir(config.outDir);
1175
+ await fs10.writeFile(resolve12(config.outDir, "manifest.json"), str, "utf-8");
1141
1176
  output.publicAssets.unshift({
1142
1177
  type: "asset",
1143
1178
  fileName: "manifest.json",
@@ -1148,30 +1183,34 @@ async function writeManifest(manifest, output, config) {
1148
1183
  }
1149
1184
  async function generateMainfest(entrypoints, buildOutput, config) {
1150
1185
  const pkg = await getPackageJson(config);
1151
- if (pkg.version == null)
1152
- throw Error("package.json does not include a version");
1153
- if (pkg.name == null)
1154
- throw Error("package.json does not include a name");
1155
- if (pkg.description == null)
1156
- throw Error("package.json does not include a description");
1157
- const manifest = {
1158
- manifest_version: config.manifestVersion,
1159
- name: pkg.name,
1160
- short_name: pkg.shortName,
1161
- version: simplifyVersion(pkg.version),
1162
- version_name: config.browser === "firefox" ? void 0 : pkg.version,
1163
- ...config.manifest
1164
- };
1186
+ const manifest = Object.assign(
1187
+ {
1188
+ manifest_version: config.manifestVersion,
1189
+ name: pkg?.name,
1190
+ description: pkg?.description,
1191
+ version: pkg?.version && simplifyVersion(pkg.version),
1192
+ // Only add the version name to chromium and if the user hasn't specified a custom version.
1193
+ version_name: config.browser !== "firefox" && !config.manifest.version ? pkg?.version : void 0,
1194
+ short_name: pkg?.shortName
1195
+ },
1196
+ config.manifest
1197
+ );
1165
1198
  addEntrypoints(manifest, entrypoints, buildOutput, config);
1166
1199
  if (config.command === "serve")
1167
1200
  addDevModeCsp(manifest, config);
1168
1201
  if (config.command === "serve")
1169
1202
  addDevModePermissions(manifest, config);
1203
+ if (manifest.name == null)
1204
+ throw Error(
1205
+ "Manifest 'name' is missing. Either:\n1. Set the name in your <root>/package.json\n2. Set a name via the manifest option in your wxt.config.ts"
1206
+ );
1207
+ if (manifest.version == null) {
1208
+ throw Error(
1209
+ "Manifest 'version' is missing. Either:\n1. Add a version in your <root>/package.json\n2. Pass the version via the manifest option in your wxt.config.ts"
1210
+ );
1211
+ }
1170
1212
  return manifest;
1171
1213
  }
1172
- async function getPackageJson(config) {
1173
- return await fs9.readJson(resolve11(config.root, "package.json"));
1174
- }
1175
1214
  function simplifyVersion(versionName) {
1176
1215
  const version3 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
1177
1216
  versionName
@@ -1415,7 +1454,7 @@ function addHostPermission(manifest, hostPermission) {
1415
1454
  // src/core/build.ts
1416
1455
  import pc2 from "picocolors";
1417
1456
  import * as vite3 from "vite";
1418
- import fs11 from "fs-extra";
1457
+ import fs12 from "fs-extra";
1419
1458
 
1420
1459
  // src/core/utils/groupEntrypoints.ts
1421
1460
  function groupEntrypoints(entrypoints) {
@@ -1463,7 +1502,13 @@ function formatDuration(duration) {
1463
1502
  }
1464
1503
 
1465
1504
  // src/core/log/printBuildSummary.ts
1466
- import path4, { relative as relative5, resolve as resolve12 } from "path";
1505
+ import { resolve as resolve13 } from "path";
1506
+
1507
+ // src/core/log/printFileList.ts
1508
+ import path4 from "node:path";
1509
+ import pc from "picocolors";
1510
+ import fs11 from "fs-extra";
1511
+ import { filesize } from "filesize";
1467
1512
 
1468
1513
  // src/core/log/printTable.ts
1469
1514
  function printTable(log, rows, gap = 2) {
@@ -1491,10 +1536,42 @@ function printTable(log, rows, gap = 2) {
1491
1536
  log(str);
1492
1537
  }
1493
1538
 
1539
+ // src/core/log/printFileList.ts
1540
+ async function printFileList(log, baseDir, files) {
1541
+ let totalSize = 0;
1542
+ const fileRows = await Promise.all(
1543
+ files.map(async (file, i) => {
1544
+ const parts = [
1545
+ path4.relative(process.cwd(), baseDir) + path4.sep,
1546
+ path4.relative(baseDir, file)
1547
+ ];
1548
+ const prefix = i === files.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
1549
+ const color = getChunkColor(file);
1550
+ const stats = await fs11.lstat(file);
1551
+ totalSize += stats.size;
1552
+ const size = String(filesize(stats.size));
1553
+ return [
1554
+ `${pc.gray(prefix)} ${pc.dim(parts[0])}${color(parts[1])}`,
1555
+ pc.dim(size)
1556
+ ];
1557
+ })
1558
+ );
1559
+ printTable(log, fileRows);
1560
+ log(`${pc.cyan("\u03A3 Total size:")} ${String(filesize(totalSize))}`);
1561
+ }
1562
+ var DEFAULT_COLOR = pc.blue;
1563
+ var CHUNK_COLORS = {
1564
+ ".js.map": pc.gray,
1565
+ ".html": pc.green,
1566
+ ".css": pc.magenta,
1567
+ ".js": pc.cyan,
1568
+ ".zip": pc.yellow
1569
+ };
1570
+ function getChunkColor(filename) {
1571
+ return Object.entries(CHUNK_COLORS).find(([key]) => filename.endsWith(key))?.[1] ?? DEFAULT_COLOR;
1572
+ }
1573
+
1494
1574
  // src/core/log/printBuildSummary.ts
1495
- import pc from "picocolors";
1496
- import fs10 from "fs-extra";
1497
- import { filesize } from "filesize";
1498
1575
  async function printBuildSummary(output, config) {
1499
1576
  const chunks = [
1500
1577
  ...output.steps.flatMap((step) => step.chunks),
@@ -1507,28 +1584,8 @@ async function printBuildSummary(output, config) {
1507
1584
  return diff;
1508
1585
  return l.fileName.localeCompare(r.fileName);
1509
1586
  });
1510
- let totalSize = 0;
1511
- const chunkRows = await Promise.all(
1512
- chunks.map(async (chunk, i) => {
1513
- const file = [
1514
- relative5(process.cwd(), config.outDir) + path4.sep,
1515
- chunk.fileName
1516
- ];
1517
- const prefix = i === chunks.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
1518
- const color = getChunkColor(chunk.fileName);
1519
- const stats = await fs10.lstat(resolve12(config.outDir, chunk.fileName));
1520
- totalSize += stats.size;
1521
- const size = String(filesize(stats.size));
1522
- return [
1523
- `${pc.gray(prefix)} ${pc.dim(file[0])}${color(file[1])}`,
1524
- pc.dim(size)
1525
- ];
1526
- })
1527
- );
1528
- printTable(config.logger.log, chunkRows);
1529
- config.logger.log(
1530
- `${pc.cyan("\u03A3 Total size:")} ${String(filesize(totalSize))}`
1531
- );
1587
+ const files = chunks.map((chunk) => resolve13(config.outDir, chunk.fileName));
1588
+ await printFileList(config.logger.log, config.outDir, files);
1532
1589
  }
1533
1590
  var DEFAULT_SORT_WEIGHT = 100;
1534
1591
  var CHUNK_SORT_WEIGHTS = {
@@ -1543,16 +1600,6 @@ function getChunkSortWeight(filename) {
1543
1600
  ([key]) => filename.endsWith(key)
1544
1601
  )?.[1] ?? DEFAULT_SORT_WEIGHT;
1545
1602
  }
1546
- var DEFAULT_COLOR = pc.blue;
1547
- var CHUNK_COLORS = {
1548
- ".js.map": pc.gray,
1549
- ".html": pc.green,
1550
- ".css": pc.magenta,
1551
- ".js": pc.cyan
1552
- };
1553
- function getChunkColor(filename) {
1554
- return Object.entries(CHUNK_COLORS).find(([key]) => filename.endsWith(key))?.[1] ?? DEFAULT_COLOR;
1555
- }
1556
1603
 
1557
1604
  // src/core/build.ts
1558
1605
  async function buildInternal(config) {
@@ -1564,8 +1611,8 @@ async function buildInternal(config) {
1564
1611
  )}`
1565
1612
  );
1566
1613
  const startTime = Date.now();
1567
- await fs11.rm(config.outDir, { recursive: true, force: true });
1568
- await fs11.ensureDir(config.outDir);
1614
+ await fs12.rm(config.outDir, { recursive: true, force: true });
1615
+ await fs12.ensureDir(config.outDir);
1569
1616
  const entrypoints = await findEntrypoints(config);
1570
1617
  const groups = groupEntrypoints(entrypoints);
1571
1618
  const { output } = await rebuild(config, groups);
@@ -1612,29 +1659,6 @@ async function rebuild(config, entrypointGroups, existingOutput = {
1612
1659
  // src/core/server.ts
1613
1660
  import * as vite4 from "vite";
1614
1661
 
1615
- // src/core/utils/findOpenPort.ts
1616
- import net from "node:net";
1617
- function findOpenPort(startPort, endPort) {
1618
- return findOpenPortRecursive(startPort, startPort, endPort);
1619
- }
1620
- function findOpenPortRecursive(port, startPort, endPort) {
1621
- return new Promise((resolve13, reject) => {
1622
- if (port > endPort)
1623
- return reject(
1624
- Error(`Could not find open port between ${startPort}-${endPort}`)
1625
- );
1626
- const server = net.createServer();
1627
- server.listen(port, () => {
1628
- server.once("close", () => resolve13(port));
1629
- server.close();
1630
- });
1631
- server.on(
1632
- "error",
1633
- () => resolve13(findOpenPortRecursive(port + 1, startPort, endPort))
1634
- );
1635
- });
1636
- }
1637
-
1638
1662
  // src/core/runners/createWebExtRunner.ts
1639
1663
  function createWebExtRunner() {
1640
1664
  let runner;
@@ -1694,7 +1718,8 @@ var ERROR_LOG_LEVEL = 50;
1694
1718
 
1695
1719
  // src/core/server.ts
1696
1720
  async function getServerInfo() {
1697
- const port = await findOpenPort(3e3, 3010);
1721
+ const { default: getPort, portNumbers } = await import("get-port");
1722
+ const port = await getPort({ port: portNumbers(3e3, 3010) });
1698
1723
  const hostname = "localhost";
1699
1724
  const origin = `http://${hostname}:${port}`;
1700
1725
  const serverConfig = {
@@ -1778,7 +1803,7 @@ function reloadHtmlPages(groups, server, config) {
1778
1803
  }
1779
1804
 
1780
1805
  // package.json
1781
- var version2 = "0.2.2";
1806
+ var version2 = "0.2.4";
1782
1807
 
1783
1808
  // src/core/utils/defineConfig.ts
1784
1809
  function defineConfig(config) {
@@ -1822,11 +1847,11 @@ async function createServer2(config) {
1822
1847
  if (changes.type === "no-change")
1823
1848
  return;
1824
1849
  internalConfig.logger.info(
1825
- `Changed: ${Array.from(new Set(fileChanges.map((change) => change[1]))).map((file) => pc3.dim(relative6(internalConfig.root, file))).join(", ")}`
1850
+ `Changed: ${Array.from(new Set(fileChanges.map((change) => change[1]))).map((file) => pc3.dim(relative5(internalConfig.root, file))).join(", ")}`
1826
1851
  );
1827
1852
  const rebuiltNames = changes.rebuildGroups.flat().map((entry) => {
1828
1853
  return pc3.cyan(
1829
- relative6(internalConfig.outDir, getEntrypointOutputFile(entry, ""))
1854
+ relative5(internalConfig.outDir, getEntrypointOutputFile(entry, ""))
1830
1855
  );
1831
1856
  }).join(pc3.dim(", "));
1832
1857
  internalConfig = await getLatestInternalConfig();