wxt 0.18.8 → 0.18.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.
@@ -3,6 +3,10 @@ import {
3
3
  some,
4
4
  toArray
5
5
  } from "./chunk-BERPNPEZ.js";
6
+ import {
7
+ addViteConfig,
8
+ defineWxtModule
9
+ } from "./chunk-6XSIWUWF.js";
6
10
  import {
7
11
  LogLevels,
8
12
  consola
@@ -12,7 +16,7 @@ import {
12
16
  } from "./chunk-QGM4M3NI.js";
13
17
 
14
18
  // package.json
15
- var version = "0.18.7";
19
+ var version = "0.18.9";
16
20
 
17
21
  // src/core/utils/paths.ts
18
22
  import systemPath from "node:path";
@@ -128,38 +132,6 @@ function download(config) {
128
132
  };
129
133
  }
130
134
 
131
- // src/core/builders/vite/plugins/unimport.ts
132
- import { createUnimport } from "unimport";
133
- import { extname } from "path";
134
- var ENABLED_EXTENSIONS = /* @__PURE__ */ new Set([
135
- ".js",
136
- ".jsx",
137
- ".ts",
138
- ".tsx",
139
- ".vue",
140
- ".svelte"
141
- ]);
142
- function unimport(config) {
143
- const options = config.imports;
144
- if (options === false) return [];
145
- const unimport2 = createUnimport(options);
146
- return {
147
- name: "wxt:unimport",
148
- async config() {
149
- await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
150
- },
151
- async transform(code, id) {
152
- if (id.includes("node_modules")) return;
153
- if (!ENABLED_EXTENSIONS.has(extname(id))) return;
154
- const injected = await unimport2.injectImports(code, id);
155
- return {
156
- code: injected.code,
157
- map: injected.s.generateMap({ hires: "boundary", source: id })
158
- };
159
- }
160
- };
161
- }
162
-
163
135
  // src/core/builders/vite/plugins/tsconfigPaths.ts
164
136
  function tsconfigPaths(config) {
165
137
  return {
@@ -428,7 +400,7 @@ function devServerGlobals(config, server) {
428
400
  }
429
401
 
430
402
  // src/core/builders/vite/plugins/multipageMove.ts
431
- import { dirname as dirname2, extname as extname2, resolve as resolve3, join } from "node:path";
403
+ import { dirname as dirname2, extname, resolve as resolve3, join } from "node:path";
432
404
  import fs, { ensureDir } from "fs-extra";
433
405
  function multipageMove(entrypoints, config) {
434
406
  return {
@@ -447,7 +419,7 @@ function multipageMove(entrypoints, config) {
447
419
  const newBundlePath = getEntrypointBundlePath(
448
420
  entrypoint,
449
421
  config.outDir,
450
- extname2(oldBundlePath)
422
+ extname(oldBundlePath)
451
423
  );
452
424
  if (newBundlePath === oldBundlePath) {
453
425
  config.logger.debug(
@@ -751,6 +723,86 @@ function removeProjectImportStatements(text) {
751
723
  ${noImports}`;
752
724
  }
753
725
 
726
+ // src/builtin-modules/unimport.ts
727
+ import { createUnimport } from "unimport";
728
+ import { extname as extname2 } from "node:path";
729
+ var unimport_default = defineWxtModule({
730
+ name: "wxt:built-in:unimport",
731
+ setup(wxt2) {
732
+ const options = wxt2.config.imports;
733
+ if (options === false) return;
734
+ let unimport;
735
+ wxt2.hooks.hook("ready", () => {
736
+ const addModuleImports = (module) => {
737
+ if (!module.imports) return;
738
+ options.imports ??= [];
739
+ options.imports.push(...module.imports);
740
+ };
741
+ wxt2.config.builtinModules.forEach(addModuleImports);
742
+ wxt2.config.userModules.forEach(addModuleImports);
743
+ });
744
+ wxt2.hooks.afterEach((event) => {
745
+ if (event.name === "ready") {
746
+ unimport = createUnimport(options);
747
+ }
748
+ });
749
+ wxt2.hooks.hook("prepare:types", async (_, entries) => {
750
+ await unimport.init();
751
+ entries.push(await getImportsDeclarationEntry(unimport));
752
+ if (!options.eslintrc.enabled) return;
753
+ entries.push(await getImportsEslintEntry(unimport, options));
754
+ });
755
+ addViteConfig(wxt2, () => ({
756
+ plugins: [vitePlugin(unimport)]
757
+ }));
758
+ }
759
+ });
760
+ function vitePlugin(unimport) {
761
+ const ENABLED_EXTENSIONS = /* @__PURE__ */ new Set([
762
+ ".js",
763
+ ".jsx",
764
+ ".ts",
765
+ ".tsx",
766
+ ".vue",
767
+ ".svelte"
768
+ ]);
769
+ return {
770
+ name: "wxt:unimport",
771
+ async transform(code, id) {
772
+ if (id.includes("node_modules")) return;
773
+ if (!ENABLED_EXTENSIONS.has(extname2(id))) return;
774
+ const injected = await unimport.injectImports(code, id);
775
+ return {
776
+ code: injected.code,
777
+ map: injected.s.generateMap({ hires: "boundary", source: id })
778
+ };
779
+ }
780
+ };
781
+ }
782
+ async function getImportsDeclarationEntry(unimport) {
783
+ await unimport.init();
784
+ return {
785
+ path: "types/imports.d.ts",
786
+ text: [
787
+ "// Generated by wxt",
788
+ await unimport.generateTypeDeclarations(),
789
+ ""
790
+ ].join("\n"),
791
+ tsReference: true
792
+ };
793
+ }
794
+ async function getImportsEslintEntry(unimport, options) {
795
+ const globals2 = {};
796
+ const eslintrc = { globals: globals2 };
797
+ (await unimport.getImports()).map((i) => i.as ?? i.name).filter(Boolean).sort().forEach((name) => {
798
+ eslintrc.globals[name] = options.eslintrc.globalsPropValue;
799
+ });
800
+ return {
801
+ path: options.eslintrc.filePath,
802
+ text: JSON.stringify(eslintrc, null, 2) + "\n"
803
+ };
804
+ }
805
+
754
806
  // src/core/utils/fs.ts
755
807
  import fs3 from "fs-extra";
756
808
  import glob from "fast-glob";
@@ -796,13 +848,17 @@ async function copyPublicDirectory() {
796
848
  await wxt.hooks.callHook("build:publicAssets", wxt, files);
797
849
  if (files.length === 0) return [];
798
850
  const publicAssets = [];
799
- for (const { absoluteSrc, relativeDest } of files) {
800
- const absoluteDest = resolve6(wxt.config.outDir, relativeDest);
851
+ for (const file of files) {
852
+ const absoluteDest = resolve6(wxt.config.outDir, file.relativeDest);
801
853
  await fs4.ensureDir(dirname3(absoluteDest));
802
- await fs4.copyFile(absoluteSrc, absoluteDest);
854
+ if ("absoluteSrc" in file) {
855
+ await fs4.copyFile(file.absoluteSrc, absoluteDest);
856
+ } else {
857
+ await fs4.writeFile(absoluteDest, file.contents, "utf8");
858
+ }
803
859
  publicAssets.push({
804
860
  type: "asset",
805
- fileName: relativeDest
861
+ fileName: file.relativeDest
806
862
  });
807
863
  }
808
864
  return publicAssets;
@@ -901,114 +957,23 @@ function findEffectedSteps(changedFile, currentOutput) {
901
957
  }
902
958
 
903
959
  // src/core/utils/building/find-entrypoints.ts
904
- import { relative as relative4, resolve as resolve8 } from "path";
905
- import fs6 from "fs-extra";
960
+ import { relative as relative3, resolve as resolve7 } from "path";
961
+ import fs5 from "fs-extra";
906
962
  import { minimatch } from "minimatch";
907
963
  import { parseHTML as parseHTML3 } from "linkedom";
908
964
  import JSON5 from "json5";
909
965
  import glob2 from "fast-glob";
910
966
  import pc2 from "picocolors";
911
-
912
- // src/core/utils/building/import-entrypoint.ts
913
- import createJITI from "jiti";
914
- import { createUnimport as createUnimport2 } from "unimport";
915
- import fs5 from "fs-extra";
916
- import { relative as relative3, resolve as resolve7 } from "node:path";
917
- import { transformSync } from "esbuild";
918
- import { fileURLToPath } from "node:url";
919
- async function importEntrypointFile(path8) {
920
- wxt.logger.debug("Loading file metadata:", path8);
921
- const normalPath = normalizePath(path8);
922
- const unimport2 = createUnimport2({
923
- ...wxt.config.imports,
924
- // Only allow specific imports, not all from the project
925
- dirs: []
926
- });
927
- await unimport2.init();
928
- const text = await fs5.readFile(path8, "utf-8");
929
- const textNoImports = removeProjectImportStatements(text);
930
- const { code } = await unimport2.injectImports(textNoImports);
931
- wxt.logger.debug(
932
- ["Text:", text, "No imports:", textNoImports, "Code:", code].join("\n")
933
- );
934
- const jiti = createJITI(
935
- typeof __filename !== "undefined" ? __filename : fileURLToPath(import.meta.url),
936
- {
937
- cache: false,
938
- debug: wxt.config.debug,
939
- esmResolve: true,
940
- alias: {
941
- "webextension-polyfill": resolve7(
942
- wxt.config.wxtModuleDir,
943
- "dist/virtual/mock-browser.js"
944
- )
945
- },
946
- // Continue using node to load TS files even if `bun run --bun` is detected. Jiti does not
947
- // respect the custom transform function when using it's native bun option.
948
- experimentalBun: false,
949
- // List of extensions to transform with esbuild
950
- extensions: [
951
- ".ts",
952
- ".cts",
953
- ".mts",
954
- ".tsx",
955
- ".js",
956
- ".cjs",
957
- ".mjs",
958
- ".jsx"
959
- ],
960
- transform(opts) {
961
- const isEntrypoint = opts.filename === normalPath;
962
- return transformSync(
963
- // Use modified source code for entrypoints
964
- isEntrypoint ? code : opts.source,
965
- getEsbuildOptions(opts)
966
- );
967
- }
968
- }
969
- );
970
- try {
971
- const res = await jiti(path8);
972
- return res.default;
973
- } catch (err) {
974
- const filePath = relative3(wxt.config.root, path8);
975
- if (err instanceof ReferenceError) {
976
- const variableName = err.message.replace(" is not defined", "");
977
- throw Error(
978
- `${filePath}: Cannot use imported variable "${variableName}" outside the main function. See https://wxt.dev/guide/go-further/entrypoint-side-effects.html`,
979
- { cause: err }
980
- );
981
- } else {
982
- wxt.logger.error(err);
983
- throw Error(`Failed to load entrypoint: ${filePath}`, { cause: err });
984
- }
985
- }
986
- }
987
- function getEsbuildOptions(opts) {
988
- const isJsx = opts.filename?.endsWith("x");
989
- return {
990
- format: "cjs",
991
- loader: isJsx ? "tsx" : "ts",
992
- ...isJsx ? {
993
- // `h` and `Fragment` are undefined, but that's OK because JSX is never evaluated while
994
- // grabbing the entrypoint's options.
995
- jsxFactory: "h",
996
- jsxFragment: "Fragment"
997
- } : void 0
998
- };
999
- }
1000
-
1001
- // src/core/utils/building/find-entrypoints.ts
1002
967
  async function findEntrypoints() {
1003
- await fs6.mkdir(wxt.config.wxtDir, { recursive: true });
1004
- await fs6.writeJson(resolve8(wxt.config.wxtDir, "tsconfig.json"), {});
968
+ await fs5.mkdir(wxt.config.wxtDir, { recursive: true });
969
+ await fs5.writeJson(resolve7(wxt.config.wxtDir, "tsconfig.json"), {});
1005
970
  const relativePaths = await glob2(Object.keys(PATH_GLOB_TO_TYPE_MAP), {
1006
971
  cwd: wxt.config.entrypointsDir
1007
972
  });
1008
973
  relativePaths.sort();
1009
974
  const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
1010
975
  const entrypointInfos = relativePaths.reduce((results, relativePath) => {
1011
- const inputPath = resolve8(wxt.config.entrypointsDir, relativePath);
976
+ const inputPath = resolve7(wxt.config.entrypointsDir, relativePath);
1012
977
  const name = getEntrypointName(wxt.config.entrypointsDir, inputPath);
1013
978
  const matchingGlob = pathGlobs.find(
1014
979
  (glob5) => minimatch(relativePath, glob5)
@@ -1050,7 +1015,7 @@ async function findEntrypoints() {
1050
1015
  return {
1051
1016
  ...info,
1052
1017
  type,
1053
- outputDir: resolve8(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
1018
+ outputDir: resolve7(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
1054
1019
  options: {
1055
1020
  include: void 0,
1056
1021
  exclude: void 0
@@ -1124,7 +1089,7 @@ function preventDuplicateEntrypointNames(files) {
1124
1089
  if (absolutePaths.length > 1) {
1125
1090
  lines.push(`- ${name}`);
1126
1091
  absolutePaths.forEach((absolutePath) => {
1127
- lines.push(` - ${relative4(wxt.config.root, absolutePath)}`);
1092
+ lines.push(` - ${relative3(wxt.config.root, absolutePath)}`);
1128
1093
  });
1129
1094
  }
1130
1095
  return lines;
@@ -1212,7 +1177,7 @@ async function getUnlistedScriptEntrypoint({
1212
1177
  name,
1213
1178
  skipped
1214
1179
  }) {
1215
- const defaultExport = await importEntrypoint(inputPath);
1180
+ const defaultExport = await wxt.builder.importEntrypoint(inputPath);
1216
1181
  if (defaultExport == null) {
1217
1182
  throw Error(
1218
1183
  `${name}: Default export not found, did you forget to call "export default defineUnlistedScript(...)"?`
@@ -1235,7 +1200,7 @@ async function getBackgroundEntrypoint({
1235
1200
  }) {
1236
1201
  let options = {};
1237
1202
  if (inputPath !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
1238
- const defaultExport = await importEntrypoint(inputPath);
1203
+ const defaultExport = await wxt.builder.importEntrypoint(inputPath);
1239
1204
  if (defaultExport == null) {
1240
1205
  throw Error(
1241
1206
  `${name}: Default export not found, did you forget to call "export default defineBackground(...)"?`
@@ -1261,7 +1226,13 @@ async function getContentScriptEntrypoint({
1261
1226
  name,
1262
1227
  skipped
1263
1228
  }) {
1264
- const { main: _, ...options } = await importEntrypoint(inputPath);
1229
+ const defaultExport = await wxt.builder.importEntrypoint(inputPath);
1230
+ if (defaultExport == null) {
1231
+ throw Error(
1232
+ `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
1233
+ );
1234
+ }
1235
+ const { main: _, ...options } = defaultExport;
1265
1236
  if (options == null) {
1266
1237
  throw Error(
1267
1238
  `${name}: Default export not found, did you forget to call "export default defineContentScript(...)"?`
@@ -1271,7 +1242,7 @@ async function getContentScriptEntrypoint({
1271
1242
  type: "content-script",
1272
1243
  name,
1273
1244
  inputPath,
1274
- outputDir: resolve8(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
1245
+ outputDir: resolve7(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR),
1275
1246
  options: resolvePerBrowserOptions(options, wxt.config.browser),
1276
1247
  skipped
1277
1248
  };
@@ -1304,7 +1275,7 @@ async function getSidepanelEntrypoint(info) {
1304
1275
  };
1305
1276
  }
1306
1277
  async function getHtmlEntrypointOptions(info, keyMap, queries, parsers) {
1307
- const content = await fs6.readFile(info.inputPath, "utf-8");
1278
+ const content = await fs5.readFile(info.inputPath, "utf-8");
1308
1279
  const { document } = parseHTML3(content);
1309
1280
  const options = {};
1310
1281
  const defaultQuery = (manifestKey) => document.querySelector(`meta[name='manifest.${manifestKey}']`)?.getAttribute("content");
@@ -1364,14 +1335,10 @@ var PATH_GLOB_TO_TYPE_MAP = {
1364
1335
  [`*/index.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style"
1365
1336
  };
1366
1337
  var CONTENT_SCRIPT_OUT_DIR = "content-scripts";
1367
- function importEntrypoint(path8) {
1368
- return wxt.config.experimental.viteRuntime ? wxt.builder.importEntrypoint(path8) : importEntrypointFile(path8);
1369
- }
1370
1338
 
1371
1339
  // src/core/utils/building/generate-wxt-dir.ts
1372
- import { createUnimport as createUnimport3 } from "unimport";
1373
- import fs7 from "fs-extra";
1374
- import { relative as relative5, resolve as resolve9 } from "path";
1340
+ import fs6 from "fs-extra";
1341
+ import { dirname as dirname4, relative as relative4, resolve as resolve8 } from "node:path";
1375
1342
  import path4 from "node:path";
1376
1343
 
1377
1344
  // src/core/utils/i18n.ts
@@ -1413,42 +1380,33 @@ function parseI18nMessages(messagesJson) {
1413
1380
 
1414
1381
  // src/core/utils/building/generate-wxt-dir.ts
1415
1382
  async function generateTypesDir(entrypoints) {
1416
- await fs7.ensureDir(wxt.config.typesDir);
1417
- const references = [];
1418
- if (wxt.config.imports !== false) {
1419
- const unimport2 = createUnimport3(wxt.config.imports);
1420
- references.push(await writeImportsDeclarationFile(unimport2));
1421
- if (wxt.config.imports.eslintrc.enabled) {
1422
- await writeImportsEslintFile(unimport2, wxt.config.imports);
1423
- }
1424
- }
1425
- references.push(await writePathsDeclarationFile(entrypoints));
1426
- references.push(await writeI18nDeclarationFile());
1427
- references.push(await writeGlobalsDeclarationFile());
1428
- const mainReference = await writeMainDeclarationFile(references);
1429
- await writeTsConfigFile(mainReference);
1430
- }
1431
- async function writeImportsDeclarationFile(unimport2) {
1432
- const filePath = resolve9(wxt.config.typesDir, "imports.d.ts");
1433
- await unimport2.scanImportsFromDir(void 0, { cwd: wxt.config.srcDir });
1434
- await writeFileIfDifferent(
1435
- filePath,
1436
- ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
1437
- "\n"
1438
- ) + "\n"
1439
- );
1440
- return filePath;
1441
- }
1442
- async function writeImportsEslintFile(unimport2, options) {
1443
- const globals2 = {};
1444
- const eslintrc = { globals: globals2 };
1445
- (await unimport2.getImports()).map((i) => i.as ?? i.name).filter(Boolean).sort().forEach((name) => {
1446
- eslintrc.globals[name] = options.eslintrc.globalsPropValue;
1383
+ await fs6.ensureDir(wxt.config.typesDir);
1384
+ const entries = [
1385
+ // Hard-coded entries
1386
+ { module: "wxt/vite-builder-env" }
1387
+ ];
1388
+ wxt.config.userModules.forEach((module) => {
1389
+ if (module.type === "node_module" && module.configKey != null)
1390
+ entries.push({ module: module.id });
1447
1391
  });
1448
- await fs7.writeJson(options.eslintrc.filePath, eslintrc, { spaces: 2 });
1392
+ entries.push(await getPathsDeclarationEntry(entrypoints));
1393
+ entries.push(await getI18nDeclarationEntry());
1394
+ entries.push(await getGlobalsDeclarationEntry());
1395
+ entries.push(await getTsConfigEntry());
1396
+ await wxt.hooks.callHook("prepare:types", wxt, entries);
1397
+ entries.push(getMainDeclarationEntry(entries));
1398
+ const absoluteFileEntries = entries.filter((entry) => "path" in entry).map((entry) => ({
1399
+ ...entry,
1400
+ path: resolve8(wxt.config.wxtDir, entry.path)
1401
+ }));
1402
+ await Promise.all(
1403
+ absoluteFileEntries.map(async (file) => {
1404
+ await fs6.ensureDir(dirname4(file.path));
1405
+ await writeFileIfDifferent(file.path, file.text);
1406
+ })
1407
+ );
1449
1408
  }
1450
- async function writePathsDeclarationFile(entrypoints) {
1451
- const filePath = resolve9(wxt.config.typesDir, "paths.d.ts");
1409
+ async function getPathsDeclarationEntry(entrypoints) {
1452
1410
  const unions = entrypoints.map(
1453
1411
  (entry) => getEntrypointBundlePath(
1454
1412
  entry,
@@ -1469,14 +1427,13 @@ declare module "wxt/browser" {
1469
1427
  }
1470
1428
  }
1471
1429
  `;
1472
- await writeFileIfDifferent(
1473
- filePath,
1474
- template.replace("{{ union }}", unions || " | never")
1475
- );
1476
- return filePath;
1430
+ return {
1431
+ path: "types/paths.d.ts",
1432
+ text: template.replace("{{ union }}", unions || " | never"),
1433
+ tsReference: true
1434
+ };
1477
1435
  }
1478
- async function writeI18nDeclarationFile() {
1479
- const filePath = resolve9(wxt.config.typesDir, "i18n.d.ts");
1436
+ async function getI18nDeclarationEntry() {
1480
1437
  const defaultLocale = wxt.config.manifest.default_locale;
1481
1438
  const template = `// Generated by wxt
1482
1439
  import "wxt/browser";
@@ -1505,7 +1462,7 @@ declare module "wxt/browser" {
1505
1462
  defaultLocale,
1506
1463
  "messages.json"
1507
1464
  );
1508
- const content = JSON.parse(await fs7.readFile(defaultLocalePath, "utf-8"));
1465
+ const content = JSON.parse(await fs6.readFile(defaultLocalePath, "utf-8"));
1509
1466
  messages = parseI18nMessages(content);
1510
1467
  } else {
1511
1468
  messages = parseI18nMessages({});
@@ -1522,18 +1479,17 @@ declare module "wxt/browser" {
1522
1479
  options?: GetMessageOptions,
1523
1480
  ): string;`;
1524
1481
  });
1525
- await writeFileIfDifferent(
1526
- filePath,
1527
- template.replace("{{ overrides }}", overrides.join("\n"))
1528
- );
1529
- return filePath;
1482
+ return {
1483
+ path: "types/i18n.d.ts",
1484
+ text: template.replace("{{ overrides }}", overrides.join("\n")),
1485
+ tsReference: true
1486
+ };
1530
1487
  }
1531
- async function writeGlobalsDeclarationFile() {
1532
- const filePath = resolve9(wxt.config.typesDir, "globals.d.ts");
1488
+ async function getGlobalsDeclarationEntry() {
1533
1489
  const globals2 = [...getGlobals(wxt.config), ...getEntrypointGlobals("")];
1534
- await writeFileIfDifferent(
1535
- filePath,
1536
- [
1490
+ return {
1491
+ path: "types/globals.d.ts",
1492
+ text: [
1537
1493
  "// Generated by wxt",
1538
1494
  "export {}",
1539
1495
  "interface ImportMetaEnv {",
@@ -1541,36 +1497,31 @@ async function writeGlobalsDeclarationFile() {
1541
1497
  "}",
1542
1498
  "interface ImportMeta {",
1543
1499
  " readonly env: ImportMetaEnv",
1544
- "}"
1545
- ].join("\n") + "\n"
1546
- );
1547
- return filePath;
1500
+ "}",
1501
+ ""
1502
+ ].join("\n"),
1503
+ tsReference: true
1504
+ };
1548
1505
  }
1549
- async function writeMainDeclarationFile(references) {
1550
- const dir = wxt.config.wxtDir;
1551
- const filePath = resolve9(dir, "wxt.d.ts");
1552
- await writeFileIfDifferent(
1553
- filePath,
1554
- [
1555
- "// Generated by wxt",
1556
- `/// <reference types="wxt/vite-builder-env" />`,
1557
- ...references.map(
1558
- (ref) => `/// <reference types="./${normalizePath(relative5(dir, ref))}" />`
1559
- ),
1560
- // Add references to modules installed from NPM to the TS project so
1561
- // their type augmentation can update InlineConfig correctly. Local
1562
- // modules defined in <root>/modules are already apart of the project, so
1563
- // we don't need to add them.
1564
- ...wxt.config.modules.filter(
1565
- (module) => module.type === "node_module" && module.configKey != null
1566
- ).map((module) => `/// <reference types="${module.id}" />`)
1567
- ].join("\n") + "\n"
1568
- );
1569
- return filePath;
1506
+ function getMainDeclarationEntry(references) {
1507
+ const lines = ["// Generated by wxt"];
1508
+ references.forEach((ref) => {
1509
+ if ("module" in ref) {
1510
+ return lines.push(`/// <reference types="${ref.module}" />`);
1511
+ } else if (ref.tsReference) {
1512
+ const absolutePath = resolve8(wxt.config.wxtDir, ref.path);
1513
+ const relativePath = relative4(wxt.config.wxtDir, absolutePath);
1514
+ lines.push(`/// <reference types="./${normalizePath(relativePath)}" />`);
1515
+ }
1516
+ });
1517
+ return {
1518
+ path: "wxt.d.ts",
1519
+ text: lines.join("\n") + "\n"
1520
+ };
1570
1521
  }
1571
- async function writeTsConfigFile(mainReference) {
1522
+ async function getTsConfigEntry() {
1572
1523
  const dir = wxt.config.wxtDir;
1573
- const getTsconfigPath = (path8) => normalizePath(relative5(dir, path8));
1524
+ const getTsconfigPath = (path8) => normalizePath(relative4(dir, path8));
1574
1525
  const paths = Object.entries(wxt.config.alias).flatMap(([alias, absolutePath]) => {
1575
1526
  const aliasPath = getTsconfigPath(absolutePath);
1576
1527
  return [
@@ -1578,9 +1529,7 @@ async function writeTsConfigFile(mainReference) {
1578
1529
  ` "${alias}/*": ["${aliasPath}/*"]`
1579
1530
  ];
1580
1531
  }).join(",\n");
1581
- await writeFileIfDifferent(
1582
- resolve9(dir, "tsconfig.json"),
1583
- `{
1532
+ const text = `{
1584
1533
  "compilerOptions": {
1585
1534
  "target": "ESNext",
1586
1535
  "module": "ESNext",
@@ -1597,11 +1546,14 @@ ${paths}
1597
1546
  },
1598
1547
  "include": [
1599
1548
  "${getTsconfigPath(wxt.config.root)}/**/*",
1600
- "./${getTsconfigPath(mainReference)}"
1549
+ "./wxt.d.ts"
1601
1550
  ],
1602
1551
  "exclude": ["${getTsconfigPath(wxt.config.outBaseDir)}"]
1603
- }`
1604
- );
1552
+ }`;
1553
+ return {
1554
+ path: "tsconfig.json",
1555
+ text
1556
+ };
1605
1557
  }
1606
1558
 
1607
1559
  // src/core/utils/building/resolve-config.ts
@@ -1609,20 +1561,20 @@ import { loadConfig } from "c12";
1609
1561
  import path5 from "node:path";
1610
1562
 
1611
1563
  // src/core/utils/cache.ts
1612
- import fs8, { ensureDir as ensureDir2 } from "fs-extra";
1613
- import { dirname as dirname4, resolve as resolve10 } from "path";
1564
+ import fs7, { ensureDir as ensureDir2 } from "fs-extra";
1565
+ import { dirname as dirname5, resolve as resolve9 } from "path";
1614
1566
  function createFsCache(wxtDir) {
1615
- const getPath = (key) => resolve10(wxtDir, "cache", encodeURIComponent(key));
1567
+ const getPath = (key) => resolve9(wxtDir, "cache", encodeURIComponent(key));
1616
1568
  return {
1617
1569
  async set(key, value) {
1618
1570
  const path8 = getPath(key);
1619
- await ensureDir2(dirname4(path8));
1571
+ await ensureDir2(dirname5(path8));
1620
1572
  await writeFileIfDifferent(path8, value);
1621
1573
  },
1622
1574
  async get(key) {
1623
1575
  const path8 = getPath(key);
1624
1576
  try {
1625
- return await fs8.readFile(path8, "utf-8");
1577
+ return await fs7.readFile(path8, "utf-8");
1626
1578
  } catch {
1627
1579
  return void 0;
1628
1580
  }
@@ -1634,12 +1586,12 @@ function createFsCache(wxtDir) {
1634
1586
  import defu from "defu";
1635
1587
 
1636
1588
  // src/core/utils/package.ts
1637
- import { resolve as resolve11 } from "node:path";
1638
- import fs9 from "fs-extra";
1589
+ import { resolve as resolve10 } from "node:path";
1590
+ import fs8 from "fs-extra";
1639
1591
  async function getPackageJson() {
1640
- const file = resolve11(wxt.config.root, "package.json");
1592
+ const file = resolve10(wxt.config.root, "package.json");
1641
1593
  try {
1642
- return await fs9.readJson(file);
1594
+ return await fs8.readJson(file);
1643
1595
  } catch (err) {
1644
1596
  wxt.logger.debug(
1645
1597
  `Failed to read package.json at: ${file}. Returning undefined.`
@@ -1655,8 +1607,13 @@ function isModuleInstalled(name) {
1655
1607
  }
1656
1608
 
1657
1609
  // src/core/utils/building/resolve-config.ts
1658
- import fs10 from "fs-extra";
1610
+ import fs9 from "fs-extra";
1659
1611
  import glob3 from "fast-glob";
1612
+
1613
+ // src/builtin-modules/index.ts
1614
+ var builtinModules = [unimport_default];
1615
+
1616
+ // src/core/utils/building/resolve-config.ts
1660
1617
  async function resolveConfig(inlineConfig, command) {
1661
1618
  let userConfig = {};
1662
1619
  let userConfigMetadata;
@@ -1733,14 +1690,20 @@ async function resolveConfig(inlineConfig, command) {
1733
1690
  hostname: "localhost"
1734
1691
  };
1735
1692
  }
1736
- const modules = await resolveWxtModules(modulesDir, mergedConfig.modules);
1737
- const moduleOptions = modules.reduce((map, module) => {
1738
- if (module.configKey) {
1739
- map[module.configKey] = // @ts-expect-error
1740
- mergedConfig[module.configKey];
1741
- }
1742
- return map;
1743
- }, {});
1693
+ const userModules = await resolveWxtUserModules(
1694
+ modulesDir,
1695
+ mergedConfig.modules
1696
+ );
1697
+ const moduleOptions = userModules.reduce(
1698
+ (map, module) => {
1699
+ if (module.configKey) {
1700
+ map[module.configKey] = // @ts-expect-error
1701
+ mergedConfig[module.configKey];
1702
+ }
1703
+ return map;
1704
+ },
1705
+ {}
1706
+ );
1744
1707
  return {
1745
1708
  browser,
1746
1709
  command,
@@ -1750,7 +1713,7 @@ async function resolveConfig(inlineConfig, command) {
1750
1713
  filterEntrypoints,
1751
1714
  env,
1752
1715
  fsCache: createFsCache(wxtDir),
1753
- imports: await getUnimportOptions(wxtDir, logger, mergedConfig),
1716
+ imports: await getUnimportOptions(wxtDir, srcDir, logger, mergedConfig),
1754
1717
  logger,
1755
1718
  manifest: await resolveManifestConfig(env, mergedConfig.manifest),
1756
1719
  manifestVersion,
@@ -1771,7 +1734,7 @@ async function resolveConfig(inlineConfig, command) {
1771
1734
  alias,
1772
1735
  experimental: defu(mergedConfig.experimental, {
1773
1736
  includeBrowserPolyfill: true,
1774
- viteRuntime: false
1737
+ entrypointImporter: "jiti"
1775
1738
  }),
1776
1739
  dev: {
1777
1740
  server: devServerConfig,
@@ -1779,7 +1742,8 @@ async function resolveConfig(inlineConfig, command) {
1779
1742
  },
1780
1743
  hooks: mergedConfig.hooks ?? {},
1781
1744
  vite: mergedConfig.vite ?? (() => ({})),
1782
- modules,
1745
+ builtinModules,
1746
+ userModules,
1783
1747
  plugins: [],
1784
1748
  ...moduleOptions
1785
1749
  };
@@ -1851,17 +1815,17 @@ function resolveAnalysisConfig(root, mergedConfig) {
1851
1815
  keepArtifacts: mergedConfig.analysis?.keepArtifacts ?? false
1852
1816
  };
1853
1817
  }
1854
- async function getUnimportOptions(wxtDir, logger, config) {
1818
+ async function getUnimportOptions(wxtDir, srcDir, logger, config) {
1855
1819
  if (config.imports === false) return false;
1856
- const enabledConfig = config.imports?.eslintrc?.enabled;
1857
- let enabled;
1858
- switch (enabledConfig) {
1820
+ const rawEslintEnabled = config.imports?.eslintrc?.enabled;
1821
+ let eslintEnabled;
1822
+ switch (rawEslintEnabled) {
1859
1823
  case void 0:
1860
1824
  case "auto":
1861
- enabled = await isModuleInstalled("eslint");
1825
+ eslintEnabled = await isModuleInstalled("eslint");
1862
1826
  break;
1863
1827
  default:
1864
- enabled = enabledConfig;
1828
+ eslintEnabled = rawEslintEnabled;
1865
1829
  }
1866
1830
  const defaultOptions = {
1867
1831
  debugLog: logger.debug,
@@ -1877,8 +1841,11 @@ async function getUnimportOptions(wxtDir, logger, config) {
1877
1841
  ],
1878
1842
  warn: logger.warn,
1879
1843
  dirs: ["components", "composables", "hooks", "utils"],
1844
+ dirsScanOptions: {
1845
+ cwd: srcDir
1846
+ },
1880
1847
  eslintrc: {
1881
- enabled,
1848
+ enabled: eslintEnabled,
1882
1849
  filePath: path5.resolve(wxtDir, "eslintrc-auto-import.json"),
1883
1850
  globalsPropValue: true
1884
1851
  }
@@ -1893,7 +1860,7 @@ async function resolveWxtModuleDir() {
1893
1860
  return path5.resolve(requireResolve("wxt"), "../..");
1894
1861
  }
1895
1862
  async function isDirMissing(dir) {
1896
- return !await fs10.exists(dir);
1863
+ return !await fs9.exists(dir);
1897
1864
  }
1898
1865
  function logMissingDir(logger, name, expected) {
1899
1866
  logger.warn(
@@ -1919,7 +1886,7 @@ async function mergeBuilderConfig(inlineConfig, userConfig) {
1919
1886
  }
1920
1887
  throw Error("Builder not found. Make sure vite is installed.");
1921
1888
  }
1922
- async function resolveWxtModules(modulesDir, modules = []) {
1889
+ async function resolveWxtUserModules(modulesDir, modules = []) {
1923
1890
  const npmModules = await Promise.all(
1924
1891
  modules.map(async (moduleId) => {
1925
1892
  const mod = await import(
@@ -2005,6 +1972,95 @@ var ENTRY_TYPE_TO_GROUP_MAP = {
2005
1972
  "content-script-style": "individual"
2006
1973
  };
2007
1974
 
1975
+ // src/core/utils/building/import-entrypoint.ts
1976
+ import createJITI from "jiti";
1977
+ import { createUnimport as createUnimport2 } from "unimport";
1978
+ import fs10 from "fs-extra";
1979
+ import { relative as relative5, resolve as resolve11 } from "node:path";
1980
+ import { transformSync } from "esbuild";
1981
+ import { fileURLToPath } from "node:url";
1982
+ async function importEntrypointFile(path8) {
1983
+ wxt.logger.debug("Loading file metadata:", path8);
1984
+ const normalPath = normalizePath(path8);
1985
+ const unimport = createUnimport2({
1986
+ ...wxt.config.imports,
1987
+ // Only allow specific imports, not all from the project
1988
+ dirs: []
1989
+ });
1990
+ await unimport.init();
1991
+ const text = await fs10.readFile(path8, "utf-8");
1992
+ const textNoImports = removeProjectImportStatements(text);
1993
+ const { code } = await unimport.injectImports(textNoImports);
1994
+ wxt.logger.debug(
1995
+ ["Text:", text, "No imports:", textNoImports, "Code:", code].join("\n")
1996
+ );
1997
+ const jiti = createJITI(
1998
+ typeof __filename !== "undefined" ? __filename : fileURLToPath(import.meta.url),
1999
+ {
2000
+ cache: false,
2001
+ debug: wxt.config.debug,
2002
+ esmResolve: true,
2003
+ alias: {
2004
+ "webextension-polyfill": resolve11(
2005
+ wxt.config.wxtModuleDir,
2006
+ "dist/virtual/mock-browser.js"
2007
+ )
2008
+ },
2009
+ // Continue using node to load TS files even if `bun run --bun` is detected. Jiti does not
2010
+ // respect the custom transform function when using it's native bun option.
2011
+ experimentalBun: false,
2012
+ // List of extensions to transform with esbuild
2013
+ extensions: [
2014
+ ".ts",
2015
+ ".cts",
2016
+ ".mts",
2017
+ ".tsx",
2018
+ ".js",
2019
+ ".cjs",
2020
+ ".mjs",
2021
+ ".jsx"
2022
+ ],
2023
+ transform(opts) {
2024
+ const isEntrypoint = opts.filename === normalPath;
2025
+ return transformSync(
2026
+ // Use modified source code for entrypoints
2027
+ isEntrypoint ? code : opts.source,
2028
+ getEsbuildOptions(opts)
2029
+ );
2030
+ }
2031
+ }
2032
+ );
2033
+ try {
2034
+ const res = await jiti(path8);
2035
+ return res.default;
2036
+ } catch (err) {
2037
+ const filePath = relative5(wxt.config.root, path8);
2038
+ if (err instanceof ReferenceError) {
2039
+ const variableName = err.message.replace(" is not defined", "");
2040
+ throw Error(
2041
+ `${filePath}: Cannot use imported variable "${variableName}" outside the main function. See https://wxt.dev/guide/go-further/entrypoint-side-effects.html`,
2042
+ { cause: err }
2043
+ );
2044
+ } else {
2045
+ wxt.logger.error(err);
2046
+ throw Error(`Failed to load entrypoint: ${filePath}`, { cause: err });
2047
+ }
2048
+ }
2049
+ }
2050
+ function getEsbuildOptions(opts) {
2051
+ const isJsx = opts.filename?.endsWith("x");
2052
+ return {
2053
+ format: "cjs",
2054
+ loader: isJsx ? "tsx" : "ts",
2055
+ ...isJsx ? {
2056
+ // `h` and `Fragment` are undefined, but that's OK because JSX is never evaluated while
2057
+ // grabbing the entrypoint's options.
2058
+ jsxFactory: "h",
2059
+ jsxFragment: "Fragment"
2060
+ } : void 0
2061
+ };
2062
+ }
2063
+
2008
2064
  // src/core/utils/building/internal-build.ts
2009
2065
  import pc5 from "picocolors";
2010
2066
  import fs13 from "fs-extra";
@@ -3125,6 +3181,9 @@ var packageManagers = {
3125
3181
  };
3126
3182
 
3127
3183
  // src/core/builders/vite/index.ts
3184
+ import { ViteNodeServer } from "vite-node/server";
3185
+ import { ViteNodeRunner } from "vite-node/client";
3186
+ import { installSourcemapsSupport } from "vite-node/source-map";
3128
3187
  async function createViteBuilder(wxtConfig, hooks, server) {
3129
3188
  const vite = await import("vite");
3130
3189
  const getBaseConfig = async () => {
@@ -3148,7 +3207,6 @@ async function createViteBuilder(wxtConfig, hooks, server) {
3148
3207
  config.plugins.push(
3149
3208
  download(wxtConfig),
3150
3209
  devHtmlPrerender(wxtConfig, server),
3151
- unimport(wxtConfig),
3152
3210
  resolveVirtualModules(wxtConfig),
3153
3211
  devServerGlobals(wxtConfig, server),
3154
3212
  tsconfigPaths(wxtConfig),
@@ -3272,21 +3330,64 @@ async function createViteBuilder(wxtConfig, hooks, server) {
3272
3330
  return {
3273
3331
  name: "Vite",
3274
3332
  version: vite.version,
3275
- async importEntrypoint(url) {
3276
- const baseConfig = await getBaseConfig();
3277
- const envConfig = {
3278
- plugins: [
3279
- webextensionPolyfillMock(wxtConfig),
3280
- removeEntrypointMainFunction(wxtConfig, url)
3281
- ]
3282
- };
3283
- const config = vite.mergeConfig(baseConfig, envConfig);
3284
- const server2 = await vite.createServer(config);
3285
- await server2.listen();
3286
- const runtime = await vite.createViteRuntime(server2, { hmr: false });
3287
- const module = await runtime.executeUrl(url);
3288
- await server2.close();
3289
- return module.default;
3333
+ async importEntrypoint(path8) {
3334
+ switch (wxtConfig.experimental.entrypointImporter) {
3335
+ default:
3336
+ case "jiti": {
3337
+ return await importEntrypointFile(path8);
3338
+ }
3339
+ case "vite-runtime": {
3340
+ const baseConfig = await getBaseConfig();
3341
+ const envConfig = {
3342
+ plugins: [
3343
+ webextensionPolyfillMock(wxtConfig),
3344
+ removeEntrypointMainFunction(wxtConfig, path8)
3345
+ ]
3346
+ };
3347
+ const config = vite.mergeConfig(baseConfig, envConfig);
3348
+ const server2 = await vite.createServer(config);
3349
+ await server2.listen();
3350
+ const runtime = await vite.createViteRuntime(server2, { hmr: false });
3351
+ const module = await runtime.executeUrl(path8);
3352
+ await server2.close();
3353
+ return module.default;
3354
+ }
3355
+ case "vite-node": {
3356
+ const baseConfig = await getBaseConfig();
3357
+ baseConfig.optimizeDeps ??= {};
3358
+ baseConfig.optimizeDeps.noDiscovery = true;
3359
+ baseConfig.optimizeDeps.include = [];
3360
+ const envConfig = {
3361
+ plugins: [
3362
+ webextensionPolyfillMock(wxtConfig),
3363
+ removeEntrypointMainFunction(wxtConfig, path8)
3364
+ ]
3365
+ };
3366
+ const config = vite.mergeConfig(baseConfig, envConfig);
3367
+ const server2 = await vite.createServer(config);
3368
+ await server2.pluginContainer.buildStart({});
3369
+ const node = new ViteNodeServer(server2);
3370
+ installSourcemapsSupport({
3371
+ getSourceMap: (source) => node.getSourceMap(source)
3372
+ });
3373
+ const runner = new ViteNodeRunner({
3374
+ root: server2.config.root,
3375
+ base: server2.config.base,
3376
+ // when having the server and runner in a different context,
3377
+ // you will need to handle the communication between them
3378
+ // and pass to this function
3379
+ fetchModule(id) {
3380
+ return node.fetchModule(id);
3381
+ },
3382
+ resolveId(id, importer) {
3383
+ return node.resolveId(id, importer);
3384
+ }
3385
+ });
3386
+ const res = await runner.executeFile(path8);
3387
+ await server2.close();
3388
+ return res.default;
3389
+ }
3390
+ }
3290
3391
  },
3291
3392
  async build(group) {
3292
3393
  let entryConfig;
@@ -3387,18 +3488,16 @@ async function registerWxt(command, inlineConfig = {}, getServer) {
3387
3488
  builder,
3388
3489
  server
3389
3490
  };
3390
- for (const module of config.modules) {
3491
+ const initModule = async (module) => {
3391
3492
  if (module.hooks) wxt.hooks.addHooks(module.hooks);
3392
- if (wxt.config.imports !== false && module.imports) {
3393
- wxt.config.imports.imports ??= [];
3394
- wxt.config.imports.imports.push(...module.imports);
3395
- }
3396
3493
  await module.setup?.(
3397
3494
  wxt,
3398
3495
  // @ts-expect-error: Untyped configKey field
3399
3496
  module.configKey ? config[module.configKey] : void 0
3400
3497
  );
3401
- }
3498
+ };
3499
+ for (const builtinModule of builtinModules) await initModule(builtinModule);
3500
+ for (const userModule of config.userModules) await initModule(userModule);
3402
3501
  wxt.hooks.addHooks(config.hooks);
3403
3502
  await wxt.hooks.callHook("ready", wxt);
3404
3503
  }
@@ -3410,11 +3509,11 @@ export {
3410
3509
  isHtmlEntrypoint,
3411
3510
  formatDuration,
3412
3511
  download,
3413
- unimport,
3414
3512
  tsconfigPaths,
3415
3513
  globals,
3416
3514
  webextensionPolyfillMock,
3417
3515
  kebabCaseAlphanumeric,
3516
+ vitePlugin,
3418
3517
  wxt,
3419
3518
  registerWxt,
3420
3519
  detectDevChanges,