wxt 0.0.2 → 0.1.1-alpha1

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.cjs CHANGED
@@ -34,8 +34,7 @@ __export(src_exports, {
34
34
  createServer: () => createServer2,
35
35
  defineConfig: () => defineConfig,
36
36
  defineRunnerConfig: () => defineRunnerConfig,
37
- rebuild: () => rebuild,
38
- version: () => version
37
+ version: () => version2
39
38
  });
40
39
  module.exports = __toCommonJS(src_exports);
41
40
 
@@ -50,24 +49,26 @@ var import_jiti = __toESM(require("jiti"), 1);
50
49
  var import_babel = __toESM(require("jiti/dist/babel"), 1);
51
50
  var import_path = require("path");
52
51
  var import_unimport = require("unimport");
52
+ var import_fs_extra = __toESM(require("fs-extra"), 1);
53
53
  async function importTsFile(root, path5) {
54
- const clientImports = await (0, import_unimport.scanExports)(
54
+ const unimport2 = (0, import_unimport.createUnimport)({});
55
+ await unimport2.scanImportsFromFile(
55
56
  (0, import_path.resolve)(root, "node_modules/wxt/dist/client.js")
56
57
  );
58
+ const text = await import_fs_extra.default.readFile(path5, "utf-8");
59
+ const res = await unimport2.injectImports(text, path5);
60
+ const transformedText = res.code;
57
61
  const jiti = (0, import_jiti.default)(__filename, {
58
62
  cache: false,
59
63
  esmResolve: true,
60
64
  interopDefault: true,
65
+ alias: {
66
+ "webextension-polyfill": "wxt"
67
+ },
61
68
  transform(opts) {
69
+ if (opts.filename === path5)
70
+ opts.source = transformedText;
62
71
  opts.source = opts.source.replace(/^import ['"].*\.css['"];?$/gm, "");
63
- opts.source = opts.source.replace(
64
- /^import\s+.*\s+from ['"]webextension-polyfill['"];?$/gm,
65
- ""
66
- );
67
- if (opts.filename === path5) {
68
- const imports = clientImports.map((i) => `import { ${i.name} } from "${i.from}";`).join("\n") + "\n";
69
- opts.source = imports + opts.source;
70
- }
71
72
  return (0, import_babel.default)(opts);
72
73
  }
73
74
  });
@@ -250,7 +251,7 @@ function download(config) {
250
251
 
251
252
  // src/core/vite-plugins/multipageMove.ts
252
253
  var import_node_path2 = require("path");
253
- var import_fs_extra = __toESM(require("fs-extra"), 1);
254
+ var import_fs_extra2 = __toESM(require("fs-extra"), 1);
254
255
  function multipageMove(entrypoints, config) {
255
256
  return {
256
257
  name: "wxt:multipage-move",
@@ -277,8 +278,8 @@ function multipageMove(entrypoints, config) {
277
278
  }
278
279
  const oldAbsPath = (0, import_node_path2.resolve)(config.outDir, oldBundlePath);
279
280
  const newAbsPath = (0, import_node_path2.resolve)(config.outDir, newBundlePath);
280
- await (0, import_fs_extra.ensureDir)((0, import_node_path2.dirname)(newAbsPath));
281
- await import_fs_extra.default.move(oldAbsPath, newAbsPath, { overwrite: true });
281
+ await (0, import_fs_extra2.ensureDir)((0, import_node_path2.dirname)(newAbsPath));
282
+ await import_fs_extra2.default.move(oldAbsPath, newAbsPath, { overwrite: true });
282
283
  const renamedChunk = {
283
284
  ...bundle[oldBundlePath],
284
285
  fileName: newBundlePath
@@ -328,7 +329,7 @@ function unimport(config) {
328
329
  }
329
330
 
330
331
  // src/core/vite-plugins/virtualEntrypoint.ts
331
- var import_fs_extra2 = __toESM(require("fs-extra"), 1);
332
+ var import_fs_extra3 = __toESM(require("fs-extra"), 1);
332
333
  var import_path3 = require("path");
333
334
  function virtualEntrypoin(type, config) {
334
335
  const virtualId = `virtual:wxt-${type}?`;
@@ -346,7 +347,7 @@ function virtualEntrypoin(type, config) {
346
347
  if (!id.startsWith(resolvedVirtualId))
347
348
  return;
348
349
  const inputPath = id.replace(resolvedVirtualId, "");
349
- const template = await import_fs_extra2.default.readFile(
350
+ const template = await import_fs_extra3.default.readFile(
350
351
  (0, import_path3.resolve)(
351
352
  config.root,
352
353
  `node_modules/wxt/dist/virtual-modules/${type}-entrypoint.js`
@@ -359,20 +360,20 @@ function virtualEntrypoin(type, config) {
359
360
  }
360
361
 
361
362
  // src/core/utils/createFsCache.ts
362
- var import_fs_extra3 = __toESM(require("fs-extra"), 1);
363
+ var import_fs_extra4 = __toESM(require("fs-extra"), 1);
363
364
  var import_path4 = require("path");
364
365
  function createFsCache(wxtDir) {
365
366
  const getPath = (key) => (0, import_path4.resolve)(wxtDir, "cache", encodeURIComponent(key));
366
367
  return {
367
368
  async set(key, value) {
368
369
  const path5 = getPath(key);
369
- await (0, import_fs_extra3.ensureDir)((0, import_path4.dirname)(path5));
370
- await import_fs_extra3.default.writeFile(path5, value, "utf-8");
370
+ await (0, import_fs_extra4.ensureDir)((0, import_path4.dirname)(path5));
371
+ await import_fs_extra4.default.writeFile(path5, value, "utf-8");
371
372
  },
372
373
  async get(key) {
373
374
  const path5 = getPath(key);
374
375
  try {
375
- return await import_fs_extra3.default.readFile(path5, "utf-8");
376
+ return await import_fs_extra4.default.readFile(path5, "utf-8");
376
377
  } catch {
377
378
  return void 0;
378
379
  }
@@ -511,15 +512,246 @@ async function getInternalConfig(config, command) {
511
512
  return finalConfig;
512
513
  }
513
514
 
515
+ // src/index.ts
516
+ var import_picocolors3 = __toESM(require("picocolors"), 1);
517
+ var vite6 = __toESM(require("vite"), 1);
518
+
519
+ // src/core/utils/arrays.ts
520
+ function every(array, predicate) {
521
+ for (let i = 0; i < array.length; i++)
522
+ if (!predicate(array[i], i))
523
+ return false;
524
+ return true;
525
+ }
526
+
527
+ // src/core/utils/detectDevChanges.ts
528
+ function detectDevChanges(changedFiles, currentOutput) {
529
+ if (currentOutput == null)
530
+ return { type: "no-change" };
531
+ const changedSteps = new Set(
532
+ changedFiles.flatMap(
533
+ (changedFile) => findEffectedSteps(changedFile, currentOutput)
534
+ )
535
+ );
536
+ if (changedSteps.size === 0)
537
+ return { type: "no-change" };
538
+ const unchangedOutput = {
539
+ manifest: currentOutput.manifest,
540
+ steps: [],
541
+ publicAssets: []
542
+ };
543
+ const changedOutput = {
544
+ manifest: currentOutput.manifest,
545
+ steps: [],
546
+ publicAssets: []
547
+ };
548
+ for (const step of currentOutput.steps) {
549
+ if (changedSteps.has(step)) {
550
+ changedOutput.steps.push(step);
551
+ } else {
552
+ unchangedOutput.steps.push(step);
553
+ }
554
+ }
555
+ for (const asset of currentOutput.publicAssets) {
556
+ if (changedSteps.has(asset)) {
557
+ changedOutput.publicAssets.push(asset);
558
+ } else {
559
+ unchangedOutput.publicAssets.push(asset);
560
+ }
561
+ }
562
+ const isOnlyHtmlChanges = changedFiles.length > 0 && every(changedFiles, ([_, file]) => file.endsWith(".html"));
563
+ if (isOnlyHtmlChanges) {
564
+ return {
565
+ type: "html-reload",
566
+ cachedOutput: unchangedOutput,
567
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
568
+ };
569
+ }
570
+ const isOnlyContentScripts = changedOutput.steps.length > 0 && every(
571
+ changedOutput.steps.flatMap((step) => step.entrypoints),
572
+ (entry) => entry.type === "content-script"
573
+ );
574
+ if (isOnlyContentScripts) {
575
+ return {
576
+ type: "content-script-reload",
577
+ cachedOutput: unchangedOutput,
578
+ changedSteps: changedOutput.steps,
579
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
580
+ };
581
+ }
582
+ return {
583
+ type: "extension-reload",
584
+ cachedOutput: unchangedOutput,
585
+ rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
586
+ };
587
+ }
588
+ function findEffectedSteps(changedFile, currentOutput) {
589
+ const changes = [];
590
+ const changedPath = changedFile[1];
591
+ const isChunkEffected = (chunk) => (
592
+ // If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
593
+ // TODO: use bundle path to support `<name>/index.html`?
594
+ chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
595
+ chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
596
+ );
597
+ for (const step of currentOutput.steps) {
598
+ const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
599
+ if (effectedChunk)
600
+ changes.push(step);
601
+ }
602
+ const effectedAsset = currentOutput.publicAssets.find(
603
+ (chunk) => isChunkEffected(chunk)
604
+ );
605
+ if (effectedAsset)
606
+ changes.push(effectedAsset);
607
+ return changes;
608
+ }
609
+
610
+ // src/index.ts
611
+ var import_async_mutex = require("async-mutex");
612
+ var import_consola3 = require("consola");
613
+ var import_node_path4 = require("path");
614
+
615
+ // src/core/build/buildEntrypoints.ts
616
+ var vite3 = __toESM(require("vite"), 1);
617
+
618
+ // src/core/utils/removeEmptyDirs.ts
619
+ var import_fs_extra5 = __toESM(require("fs-extra"), 1);
620
+ var import_path5 = __toESM(require("path"), 1);
621
+ async function removeEmptyDirs(dir) {
622
+ const files = await import_fs_extra5.default.readdir(dir);
623
+ for (const file of files) {
624
+ const filePath = import_path5.default.join(dir, file);
625
+ const stats = await import_fs_extra5.default.stat(filePath);
626
+ if (stats.isDirectory()) {
627
+ await removeEmptyDirs(filePath);
628
+ }
629
+ }
630
+ try {
631
+ await import_fs_extra5.default.rmdir(dir);
632
+ } catch {
633
+ }
634
+ }
635
+
636
+ // src/core/build/buildEntrypoints.ts
637
+ var import_fast_glob = __toESM(require("fast-glob"), 1);
638
+ var import_fs_extra6 = __toESM(require("fs-extra"), 1);
639
+ var import_path6 = require("path");
640
+ async function buildEntrypoints(groups, config) {
641
+ const steps = [];
642
+ for (const group of groups) {
643
+ const step = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config);
644
+ steps.push(step);
645
+ }
646
+ const publicAssets = await copyPublicDirectory(config);
647
+ await removeEmptyDirs(config.outDir);
648
+ return { publicAssets, steps };
649
+ }
650
+ async function buildSingleEntrypoint(entrypoint, config) {
651
+ const isVirtual = ["background", "content-script"].includes(entrypoint.type);
652
+ const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
653
+ const libMode = {
654
+ build: {
655
+ lib: {
656
+ entry,
657
+ formats: ["iife"],
658
+ name: entrypoint.name,
659
+ fileName: entrypoint.name
660
+ },
661
+ rollupOptions: {
662
+ output: {
663
+ // There's only a single output for this build, so we use the desired bundle path for the
664
+ // entry output (like "content-scripts/overlay.js")
665
+ entryFileNames: getEntrypointBundlePath(
666
+ entrypoint,
667
+ config.outDir,
668
+ ".js"
669
+ ),
670
+ // Output content script CSS to assets/ with a hash to prevent conflicts. Defaults to
671
+ // "[name].[ext]" in lib mode, which usually results in "style.css". That means multiple
672
+ // content scripts with styles would overwrite each other if it weren't changed below.
673
+ assetFileNames: `assets/${entrypoint.name}.[ext]`
674
+ }
675
+ }
676
+ }
677
+ };
678
+ const entryConfig = vite3.mergeConfig(
679
+ libMode,
680
+ config.vite
681
+ );
682
+ const result = await vite3.build(entryConfig);
683
+ return {
684
+ entrypoints: entrypoint,
685
+ chunks: getBuildOutputChunks(result)
686
+ };
687
+ }
688
+ async function buildMultipleEntrypoints(entrypoints, config) {
689
+ const multiPage = {
690
+ plugins: [multipageMove(entrypoints, config)],
691
+ build: {
692
+ rollupOptions: {
693
+ input: entrypoints.reduce((input, entry) => {
694
+ input[entry.name] = entry.inputPath;
695
+ return input;
696
+ }, {}),
697
+ output: {
698
+ // Include a hash to prevent conflicts
699
+ chunkFileNames: "chunks/[name]-[hash].js",
700
+ // Include a hash to prevent conflicts
701
+ entryFileNames: "chunks/[name]-[hash].js",
702
+ // We can't control the "name", so we need a hash to prevent conflicts
703
+ assetFileNames: "assets/[name]-[hash].[ext]"
704
+ }
705
+ }
706
+ }
707
+ };
708
+ const entryConfig = vite3.mergeConfig(
709
+ multiPage,
710
+ config.vite
711
+ );
712
+ const result = await vite3.build(entryConfig);
713
+ return {
714
+ entrypoints,
715
+ chunks: getBuildOutputChunks(result)
716
+ };
717
+ }
718
+ function getBuildOutputChunks(result) {
719
+ if ("on" in result)
720
+ throw Error("wxt does not support vite watch mode.");
721
+ if (Array.isArray(result))
722
+ return result.flatMap(({ output }) => output);
723
+ return result.output;
724
+ }
725
+ async function copyPublicDirectory(config) {
726
+ const publicAssets = [];
727
+ if (!await import_fs_extra6.default.exists(config.publicDir))
728
+ return publicAssets;
729
+ const files = await (0, import_fast_glob.default)("**/*", { cwd: config.publicDir });
730
+ for (const file of files) {
731
+ const srcPath = (0, import_path6.resolve)(config.publicDir, file);
732
+ const outPath = (0, import_path6.resolve)(config.outDir, file);
733
+ await import_fs_extra6.default.ensureDir((0, import_path6.dirname)(outPath));
734
+ await import_fs_extra6.default.copyFile(srcPath, outPath);
735
+ publicAssets.push({
736
+ type: "asset",
737
+ fileName: file,
738
+ name: file,
739
+ needsCodeReference: false,
740
+ source: await import_fs_extra6.default.readFile(srcPath)
741
+ });
742
+ }
743
+ return publicAssets;
744
+ }
745
+
514
746
  // src/core/build/findEntrypoints.ts
515
- var import_path5 = require("path");
516
- var import_fs_extra4 = __toESM(require("fs-extra"), 1);
747
+ var import_path7 = require("path");
748
+ var import_fs_extra7 = __toESM(require("fs-extra"), 1);
517
749
  var import_picomatch = __toESM(require("picomatch"), 1);
518
750
  var import_linkedom2 = require("linkedom");
519
751
  var import_json5 = __toESM(require("json5"), 1);
520
- var import_fast_glob = __toESM(require("fast-glob"), 1);
752
+ var import_fast_glob2 = __toESM(require("fast-glob"), 1);
521
753
  async function findEntrypoints(config) {
522
- const relativePaths = await (0, import_fast_glob.default)("**/*", {
754
+ const relativePaths = await (0, import_fast_glob2.default)("**/*", {
523
755
  cwd: config.entrypointsDir
524
756
  });
525
757
  relativePaths.sort();
@@ -528,7 +760,7 @@ async function findEntrypoints(config) {
528
760
  const entrypoints = [];
529
761
  await Promise.all(
530
762
  relativePaths.map(async (relativePath) => {
531
- const path5 = (0, import_path5.resolve)(config.entrypointsDir, relativePath);
763
+ const path5 = (0, import_path7.resolve)(config.entrypointsDir, relativePath);
532
764
  const matchingGlob = pathGlobs.find(
533
765
  (glob3) => import_picomatch.default.isMatch(relativePath, glob3)
534
766
  );
@@ -575,8 +807,8 @@ ${JSON.stringify(
575
807
  if (withSameName) {
576
808
  throw Error(
577
809
  `Multiple entrypoints with the name "${entrypoint.name}" detected, but only one is allowed: ${[
578
- (0, import_path5.relative)(config.root, withSameName.inputPath),
579
- (0, import_path5.relative)(config.root, entrypoint.inputPath)
810
+ (0, import_path7.relative)(config.root, withSameName.inputPath),
811
+ (0, import_path7.relative)(config.root, entrypoint.inputPath)
580
812
  ].join(", ")}`
581
813
  );
582
814
  }
@@ -588,7 +820,7 @@ ${JSON.stringify(
588
820
  }
589
821
  async function getPopupEntrypoint(config, path5) {
590
822
  const options = {};
591
- const content = await import_fs_extra4.default.readFile(path5, "utf-8");
823
+ const content = await import_fs_extra7.default.readFile(path5, "utf-8");
592
824
  const { document } = (0, import_linkedom2.parseHTML)(content);
593
825
  const title = document.querySelector("title");
594
826
  if (title != null)
@@ -618,7 +850,7 @@ async function getPopupEntrypoint(config, path5) {
618
850
  }
619
851
  async function getOptionsEntrypoint(config, path5) {
620
852
  const options = {};
621
- const content = await import_fs_extra4.default.readFile(path5, "utf-8");
853
+ const content = await import_fs_extra7.default.readFile(path5, "utf-8");
622
854
  const { document } = (0, import_linkedom2.parseHTML)(content);
623
855
  const openInTabContent = document.querySelector("meta[name='manifest.open_in_tab']")?.getAttribute("content");
624
856
  if (openInTabContent) {
@@ -665,7 +897,7 @@ async function getContentScriptEntrypoint(config, name, path5) {
665
897
  type: "content-script",
666
898
  name: getEntrypointName(config.entrypointsDir, path5),
667
899
  inputPath: path5,
668
- outputDir: (0, import_path5.resolve)(config.outDir, "content-scripts"),
900
+ outputDir: (0, import_path7.resolve)(config.outDir, "content-scripts"),
669
901
  options
670
902
  };
671
903
  }
@@ -700,140 +932,112 @@ var PATH_GLOB_TO_TYPE_MAP = {
700
932
  "*/*": "ignored"
701
933
  };
702
934
 
703
- // src/core/build/buildEntrypoints.ts
704
- var vite3 = __toESM(require("vite"), 1);
705
-
706
- // src/core/utils/removeEmptyDirs.ts
707
- var import_fs_extra5 = __toESM(require("fs-extra"), 1);
708
- var import_path6 = __toESM(require("path"), 1);
709
- async function removeEmptyDirs(dir) {
710
- const files = await import_fs_extra5.default.readdir(dir);
711
- for (const file of files) {
712
- const filePath = import_path6.default.join(dir, file);
713
- const stats = await import_fs_extra5.default.stat(filePath);
714
- if (stats.isDirectory()) {
715
- await removeEmptyDirs(filePath);
716
- }
717
- }
718
- try {
719
- await import_fs_extra5.default.rmdir(dir);
720
- } catch {
721
- }
935
+ // src/core/build/generateTypesDir.ts
936
+ var import_unimport3 = require("unimport");
937
+ var import_fs_extra8 = __toESM(require("fs-extra"), 1);
938
+ var import_path8 = require("path");
939
+ async function generateTypesDir(entrypoints, config) {
940
+ await import_fs_extra8.default.ensureDir(config.typesDir);
941
+ const references = [];
942
+ references.push(await writeImportsDeclarationFile(config));
943
+ references.push(await writePathsDeclarationFile(entrypoints, config));
944
+ references.push(await writeGlobalsDeclarationFile(config));
945
+ const mainReference = await writeMainDeclarationFile(references, config);
946
+ await writeTsConfigFile(mainReference, config);
722
947
  }
723
-
724
- // src/core/build/buildEntrypoints.ts
725
- var import_fast_glob2 = __toESM(require("fast-glob"), 1);
726
- var import_fs_extra6 = __toESM(require("fs-extra"), 1);
727
- var import_path7 = require("path");
728
- async function buildEntrypoints(groups, config) {
729
- const steps = [];
730
- for (const group of groups) {
731
- const step = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config);
732
- steps.push(step);
733
- }
734
- const publicAssets = await copyPublicDirectory(config);
735
- await removeEmptyDirs(config.outDir);
736
- return { publicAssets, steps };
948
+ async function writeImportsDeclarationFile(config) {
949
+ const filePath = (0, import_path8.resolve)(config.typesDir, "imports.d.ts");
950
+ const unimport2 = (0, import_unimport3.createUnimport)(getUnimportOptions(config));
951
+ await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
952
+ await import_fs_extra8.default.writeFile(
953
+ filePath,
954
+ ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
955
+ "\n"
956
+ ) + "\n"
957
+ );
958
+ return filePath;
737
959
  }
738
- async function buildSingleEntrypoint(entrypoint, config) {
739
- const isVirtual = ["background", "content-script"].includes(entrypoint.type);
740
- const entry = isVirtual ? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}` : entrypoint.inputPath;
741
- const libMode = {
742
- build: {
743
- lib: {
744
- entry,
745
- formats: ["iife"],
746
- name: entrypoint.name,
747
- fileName: entrypoint.name
748
- },
749
- rollupOptions: {
750
- output: {
751
- // There's only a single output for this build, so we use the desired bundle path for the
752
- // entry output (like "content-scripts/overlay.js")
753
- entryFileNames: getEntrypointBundlePath(
754
- entrypoint,
755
- config.outDir,
756
- ".js"
757
- ),
758
- // Output content script CSS to assets/ with a hash to prevent conflicts. Defaults to
759
- // "[name].[ext]" in lib mode, which usually results in "style.css". That means multiple
760
- // content scripts with styles would overwrite each other if it weren't changed below.
761
- assetFileNames: `assets/${entrypoint.name}.[ext]`
762
- }
763
- }
764
- }
765
- };
766
- const entryConfig = vite3.mergeConfig(
767
- libMode,
768
- config.vite
960
+ async function writePathsDeclarationFile(entrypoints, config) {
961
+ const filePath = (0, import_path8.resolve)(config.typesDir, "paths.d.ts");
962
+ await import_fs_extra8.default.writeFile(
963
+ filePath,
964
+ [
965
+ "// Generated by wxt",
966
+ "type EntrypointPath =",
967
+ ...entrypoints.map((entry) => {
968
+ const path5 = getEntrypointBundlePath(
969
+ entry,
970
+ config.outDir,
971
+ entry.inputPath.endsWith(".html") ? ".html" : ".js"
972
+ );
973
+ return ` | "/${path5}"`;
974
+ }).sort()
975
+ ].join("\n") + "\n"
769
976
  );
770
- const result = await vite3.build(entryConfig);
771
- return {
772
- entrypoints: entrypoint,
773
- chunks: getBuildOutputChunks(result)
774
- };
977
+ return filePath;
775
978
  }
776
- async function buildMultipleEntrypoints(entrypoints, config) {
777
- const multiPage = {
778
- plugins: [multipageMove(entrypoints, config)],
779
- build: {
780
- rollupOptions: {
781
- input: entrypoints.reduce((input, entry) => {
782
- input[entry.name] = entry.inputPath;
783
- return input;
784
- }, {}),
785
- output: {
786
- // Include a hash to prevent conflicts
787
- chunkFileNames: "chunks/[name]-[hash].js",
788
- // Include a hash to prevent conflicts
789
- entryFileNames: "chunks/[name]-[hash].js",
790
- // We can't control the "name", so we need a hash to prevent conflicts
791
- assetFileNames: "assets/[name]-[hash].[ext]"
792
- }
793
- }
794
- }
795
- };
796
- const entryConfig = vite3.mergeConfig(
797
- multiPage,
798
- config.vite
979
+ async function writeGlobalsDeclarationFile(config) {
980
+ const filePath = (0, import_path8.resolve)(config.typesDir, "globals.d.ts");
981
+ const globals = getGlobals(config);
982
+ await import_fs_extra8.default.writeFile(
983
+ filePath,
984
+ [
985
+ "// Generated by wxt",
986
+ "export {}",
987
+ "declare global {",
988
+ ...globals.map((global) => ` const ${global.name}: ${global.type};`),
989
+ "}"
990
+ ].join("\n") + "\n",
991
+ "utf-8"
799
992
  );
800
- const result = await vite3.build(entryConfig);
801
- return {
802
- entrypoints,
803
- chunks: getBuildOutputChunks(result)
804
- };
993
+ return filePath;
805
994
  }
806
- function getBuildOutputChunks(result) {
807
- if ("on" in result)
808
- throw Error("wxt does not support vite watch mode.");
809
- if (Array.isArray(result))
810
- return result.flatMap(({ output }) => output);
811
- return result.output;
995
+ async function writeMainDeclarationFile(references, config) {
996
+ const dir = config.wxtDir;
997
+ const filePath = (0, import_path8.resolve)(dir, "wxt.d.ts");
998
+ await import_fs_extra8.default.writeFile(
999
+ filePath,
1000
+ [
1001
+ "// Generated by wxt",
1002
+ ...references.map(
1003
+ (ref) => `/// <reference types="./${(0, import_path8.relative)(dir, ref)}" />`
1004
+ )
1005
+ ].join("\n") + "\n"
1006
+ );
1007
+ return filePath;
812
1008
  }
813
- async function copyPublicDirectory(config) {
814
- const publicAssets = [];
815
- if (!await import_fs_extra6.default.exists(config.publicDir))
816
- return publicAssets;
817
- const files = await (0, import_fast_glob2.default)("**/*", { cwd: config.publicDir });
818
- for (const file of files) {
819
- const srcPath = (0, import_path7.resolve)(config.publicDir, file);
820
- const outPath = (0, import_path7.resolve)(config.outDir, file);
821
- await import_fs_extra6.default.ensureDir((0, import_path7.dirname)(outPath));
822
- await import_fs_extra6.default.copyFile(srcPath, outPath);
823
- publicAssets.push({
824
- type: "asset",
825
- fileName: file,
826
- name: file,
827
- needsCodeReference: false,
828
- source: await import_fs_extra6.default.readFile(srcPath)
829
- });
830
- }
831
- return publicAssets;
1009
+ async function writeTsConfigFile(mainReference, config) {
1010
+ const dir = config.wxtDir;
1011
+ await import_fs_extra8.default.writeFile(
1012
+ (0, import_path8.resolve)(dir, "tsconfig.json"),
1013
+ `{
1014
+ "compilerOptions": {
1015
+ "target": "ESNext",
1016
+ "module": "ESNext",
1017
+ "moduleResolution": "Bundler",
1018
+ "noEmit": true,
1019
+ "esModuleInterop": true,
1020
+ "forceConsistentCasingInFileNames": true,
1021
+ "resolveJsonModule": true,
1022
+
1023
+ /* Type Checking */
1024
+ "strict": true,
1025
+
1026
+ /* Completeness */
1027
+ "skipLibCheck": true
1028
+ },
1029
+ "include": [
1030
+ "${(0, import_path8.relative)(dir, config.root)}/**/*",
1031
+ "./${(0, import_path8.relative)(dir, mainReference)}"
1032
+ ],
1033
+ "exclude": ["${(0, import_path8.relative)(dir, config.outBaseDir)}"]
1034
+ }`
1035
+ );
832
1036
  }
833
1037
 
834
1038
  // src/core/utils/manifest.ts
835
- var import_fs_extra7 = __toESM(require("fs-extra"), 1);
836
- var import_path8 = require("path");
1039
+ var import_fs_extra9 = __toESM(require("fs-extra"), 1);
1040
+ var import_path9 = require("path");
837
1041
 
838
1042
  // src/core/utils/ContentSecurityPolicy.ts
839
1043
  var ContentSecurityPolicy = class _ContentSecurityPolicy {
@@ -881,8 +1085,8 @@ var ContentSecurityPolicy = class _ContentSecurityPolicy {
881
1085
  // src/core/utils/manifest.ts
882
1086
  async function writeManifest(manifest, output, config) {
883
1087
  const str = config.mode === "production" ? JSON.stringify(manifest) : JSON.stringify(manifest, null, 2);
884
- await import_fs_extra7.default.ensureDir(config.outDir);
885
- await import_fs_extra7.default.writeFile((0, import_path8.resolve)(config.outDir, "manifest.json"), str, "utf-8");
1088
+ await import_fs_extra9.default.ensureDir(config.outDir);
1089
+ await import_fs_extra9.default.writeFile((0, import_path9.resolve)(config.outDir, "manifest.json"), str, "utf-8");
886
1090
  output.publicAssets.unshift({
887
1091
  type: "asset",
888
1092
  fileName: "manifest.json",
@@ -910,10 +1114,12 @@ async function generateMainfest(entrypoints, buildOutput, config) {
910
1114
  addEntrypoints(manifest, entrypoints, buildOutput, config);
911
1115
  if (config.command === "serve")
912
1116
  addDevModeCsp(manifest, config);
1117
+ if (config.command === "serve")
1118
+ addDevModePermissions(manifest, config);
913
1119
  return manifest;
914
1120
  }
915
1121
  async function getPackageJson(config) {
916
- return await import_fs_extra7.default.readJson((0, import_path8.resolve)(config.root, "package.json"));
1122
+ return await import_fs_extra9.default.readJson((0, import_path9.resolve)(config.root, "package.json"));
917
1123
  }
918
1124
  function simplifyVersion(versionName) {
919
1125
  const version3 = /^((0|[1-9][0-9]{0,8})([.](0|[1-9][0-9]{0,8})){0,3}).*$/.exec(
@@ -1066,23 +1272,23 @@ function addEntrypoints(manifest, entrypoints, buildOutput, config) {
1066
1272
  }
1067
1273
  }
1068
1274
  if (contentScripts?.length) {
1069
- if (config.command === "serve") {
1070
- const permissionsKey = config.manifestVersion === 2 ? "permissions" : "host_permissions";
1071
- const hostPermissions = new Set(manifest[permissionsKey] ?? []);
1275
+ if (config.command === "serve" && config.manifestVersion === 3) {
1276
+ const hostPermissions = new Set(manifest.host_permissions ?? []);
1072
1277
  contentScripts.forEach((script) => {
1073
1278
  script.options.matches.forEach((matchPattern) => {
1074
1279
  hostPermissions.add(matchPattern);
1075
1280
  });
1076
1281
  });
1077
- manifest[permissionsKey] = Array.from(hostPermissions).sort();
1282
+ hostPermissions.forEach(
1283
+ (permission) => addHostPermission(manifest, permission)
1284
+ );
1078
1285
  } else {
1079
1286
  const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
1080
1287
  const hash = JSON.stringify(script.options);
1081
- if (!map.has(hash)) {
1082
- map.set(hash, [script]);
1083
- } else {
1288
+ if (map.has(hash))
1084
1289
  map.get(hash)?.push(script);
1085
- }
1290
+ else
1291
+ map.set(hash, [script]);
1086
1292
  return map;
1087
1293
  }, /* @__PURE__ */ new Map());
1088
1294
  manifest.content_scripts = Array.from(hashToEntrypointsMap.entries()).map(
@@ -1103,13 +1309,9 @@ function addDevModeCsp(manifest, config) {
1103
1309
  const permission = `http://${config.server?.hostname ?? ""}/*`;
1104
1310
  const allowedCsp = config.server?.origin ?? "http://localhost:*";
1105
1311
  if (manifest.manifest_version === 3) {
1106
- manifest.host_permissions ??= [];
1107
- if (!manifest.host_permissions.includes(permission))
1108
- manifest.host_permissions.push(permission);
1312
+ addHostPermission(manifest, permission);
1109
1313
  } else {
1110
- manifest.permissions ??= [];
1111
- if (!manifest.permissions.includes(permission))
1112
- manifest.permissions.push(permission);
1314
+ addPermission(manifest, permission);
1113
1315
  }
1114
1316
  const csp = new ContentSecurityPolicy(
1115
1317
  manifest.manifest_version === 3 ? (
@@ -1127,6 +1329,11 @@ function addDevModeCsp(manifest, config) {
1127
1329
  manifest.content_security_policy = csp.toString();
1128
1330
  }
1129
1331
  }
1332
+ function addDevModePermissions(manifest, config) {
1333
+ addPermission(manifest, "tabs");
1334
+ if (config.manifestVersion === 3)
1335
+ addPermission(manifest, "scripting");
1336
+ }
1130
1337
  function getContentScriptCssFiles(contentScripts, buildOutput) {
1131
1338
  const css = [];
1132
1339
  const allChunks = buildOutput.steps.flatMap((step) => step.chunks);
@@ -1141,9 +1348,71 @@ function getContentScriptCssFiles(contentScripts, buildOutput) {
1141
1348
  return css;
1142
1349
  return void 0;
1143
1350
  }
1351
+ function addPermission(manifest, permission) {
1352
+ manifest.permissions ??= [];
1353
+ if (manifest.permissions.includes(permission))
1354
+ return;
1355
+ manifest.permissions.push(permission);
1356
+ }
1357
+ function addHostPermission(manifest, hostPermission) {
1358
+ manifest.host_permissions ??= [];
1359
+ if (manifest.host_permissions.includes(hostPermission))
1360
+ return;
1361
+ manifest.host_permissions.push(hostPermission);
1362
+ }
1363
+
1364
+ // src/core/build.ts
1365
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
1366
+ var vite4 = __toESM(require("vite"), 1);
1367
+ var import_fs_extra11 = __toESM(require("fs-extra"), 1);
1368
+
1369
+ // src/core/utils/groupEntrypoints.ts
1370
+ function groupEntrypoints(entrypoints) {
1371
+ const groupIndexMap = {};
1372
+ const groups = [];
1373
+ for (const entry of entrypoints) {
1374
+ const group = ENTRY_TYPE_TO_GROUP_MAP[entry.type];
1375
+ if (group === "no-group") {
1376
+ groups.push(entry);
1377
+ } else {
1378
+ let groupIndex = groupIndexMap[group];
1379
+ if (groupIndex == null) {
1380
+ groupIndex = groups.push([]) - 1;
1381
+ groupIndexMap[group] = groupIndex;
1382
+ }
1383
+ groups[groupIndex].push(entry);
1384
+ }
1385
+ }
1386
+ return groups;
1387
+ }
1388
+ var ENTRY_TYPE_TO_GROUP_MAP = {
1389
+ sandbox: "sandbox-page",
1390
+ popup: "extension-page",
1391
+ newtab: "extension-page",
1392
+ history: "extension-page",
1393
+ options: "extension-page",
1394
+ devtools: "extension-page",
1395
+ bookmarks: "extension-page",
1396
+ sidepanel: "extension-page",
1397
+ "unlisted-page": "extension-page",
1398
+ background: "no-group",
1399
+ "content-script": "no-group",
1400
+ "unlisted-script": "no-group"
1401
+ };
1402
+
1403
+ // src/core/utils/formatDuration.ts
1404
+ function formatDuration(duration) {
1405
+ if (duration < 1e3)
1406
+ return `${duration} ms`;
1407
+ if (duration < 1e4)
1408
+ return `${(duration / 1e3).toFixed(3)} s`;
1409
+ if (duration < 6e4)
1410
+ return `${(duration / 1e3).toFixed(1)} s`;
1411
+ return `${(duration / 1e3).toFixed(0)} s`;
1412
+ }
1144
1413
 
1145
1414
  // src/core/log/printBuildSummary.ts
1146
- var import_path9 = __toESM(require("path"), 1);
1415
+ var import_path10 = __toESM(require("path"), 1);
1147
1416
 
1148
1417
  // src/core/log/printTable.ts
1149
1418
  function printTable(log, rows, gap = 2) {
@@ -1173,15 +1442,15 @@ function printTable(log, rows, gap = 2) {
1173
1442
 
1174
1443
  // src/core/log/printBuildSummary.ts
1175
1444
  var import_picocolors = __toESM(require("picocolors"), 1);
1176
- var import_fs_extra8 = __toESM(require("fs-extra"), 1);
1445
+ var import_fs_extra10 = __toESM(require("fs-extra"), 1);
1177
1446
  var import_filesize = require("filesize");
1178
1447
  async function printBuildSummary(output, config) {
1179
1448
  const chunks = [
1180
1449
  ...output.steps.flatMap((step) => step.chunks),
1181
1450
  ...output.publicAssets
1182
1451
  ].sort((l, r) => {
1183
- const lWeight = CHUNK_SORT_WEIGHTS[l.fileName] ?? CHUNK_SORT_WEIGHTS[(0, import_path9.extname)(l.fileName)] ?? DEFAULT_SORT_WEIGHT;
1184
- const rWeight = CHUNK_SORT_WEIGHTS[r.fileName] ?? CHUNK_SORT_WEIGHTS[(0, import_path9.extname)(r.fileName)] ?? DEFAULT_SORT_WEIGHT;
1452
+ const lWeight = CHUNK_SORT_WEIGHTS[l.fileName] ?? CHUNK_SORT_WEIGHTS[(0, import_path10.extname)(l.fileName)] ?? DEFAULT_SORT_WEIGHT;
1453
+ const rWeight = CHUNK_SORT_WEIGHTS[r.fileName] ?? CHUNK_SORT_WEIGHTS[(0, import_path10.extname)(r.fileName)] ?? DEFAULT_SORT_WEIGHT;
1185
1454
  const diff = lWeight - rWeight;
1186
1455
  if (diff !== 0)
1187
1456
  return diff;
@@ -1191,13 +1460,13 @@ async function printBuildSummary(output, config) {
1191
1460
  const chunkRows = await Promise.all(
1192
1461
  chunks.map(async (chunk, i) => {
1193
1462
  const file = [
1194
- (0, import_path9.relative)(process.cwd(), config.outDir) + import_path9.default.sep,
1463
+ (0, import_path10.relative)(process.cwd(), config.outDir) + import_path10.default.sep,
1195
1464
  chunk.fileName
1196
1465
  ];
1197
- const ext = (0, import_path9.extname)(chunk.fileName);
1466
+ const ext = (0, import_path10.extname)(chunk.fileName);
1198
1467
  const prefix = i === chunks.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
1199
1468
  const color = CHUNK_COLORS[ext] ?? DEFAULT_COLOR;
1200
- const stats = await import_fs_extra8.default.lstat((0, import_path9.resolve)(config.outDir, chunk.fileName));
1469
+ const stats = await import_fs_extra10.default.lstat((0, import_path10.resolve)(config.outDir, chunk.fileName));
1201
1470
  totalSize += stats.size;
1202
1471
  const size = String((0, import_filesize.filesize)(stats.size));
1203
1472
  return [
@@ -1225,115 +1494,63 @@ var CHUNK_COLORS = {
1225
1494
  ".js": import_picocolors.default.cyan
1226
1495
  };
1227
1496
 
1228
- // src/index.ts
1229
- var import_fs_extra10 = __toESM(require("fs-extra"), 1);
1230
-
1231
- // src/core/build/generateTypesDir.ts
1232
- var import_unimport3 = require("unimport");
1233
- var import_fs_extra9 = __toESM(require("fs-extra"), 1);
1234
- var import_path10 = require("path");
1235
- async function generateTypesDir(entrypoints, config) {
1236
- await import_fs_extra9.default.ensureDir(config.typesDir);
1237
- const references = [];
1238
- references.push(await writeImportsDeclarationFile(config));
1239
- references.push(await writePathsDeclarationFile(entrypoints, config));
1240
- references.push(await writeGlobalsDeclarationFile(config));
1241
- const mainReference = await writeMainDeclarationFile(references, config);
1242
- await writeTsConfigFile(mainReference, config);
1243
- }
1244
- async function writeImportsDeclarationFile(config) {
1245
- const filePath = (0, import_path10.resolve)(config.typesDir, "imports.d.ts");
1246
- const unimport2 = (0, import_unimport3.createUnimport)(getUnimportOptions(config));
1247
- await unimport2.scanImportsFromDir(void 0, { cwd: config.srcDir });
1248
- await import_fs_extra9.default.writeFile(
1249
- filePath,
1250
- ["// Generated by wxt", await unimport2.generateTypeDeclarations()].join(
1251
- "\n"
1252
- ) + "\n"
1497
+ // src/core/build.ts
1498
+ async function buildInternal(config) {
1499
+ const verb = config.command === "serve" ? "Pre-rendering" : "Building";
1500
+ const target = `${config.browser}-mv${config.manifestVersion}`;
1501
+ config.logger.info(
1502
+ `${verb} ${import_picocolors2.default.cyan(target)} for ${import_picocolors2.default.cyan(config.mode)} with ${import_picocolors2.default.green(
1503
+ `Vite ${vite4.version}`
1504
+ )}`
1253
1505
  );
1254
- return filePath;
1255
- }
1256
- async function writePathsDeclarationFile(entrypoints, config) {
1257
- const filePath = (0, import_path10.resolve)(config.typesDir, "paths.d.ts");
1258
- await import_fs_extra9.default.writeFile(
1259
- filePath,
1260
- [
1261
- "// Generated by wxt",
1262
- "type EntrypointPath =",
1263
- ...entrypoints.map((entry) => {
1264
- const path5 = getEntrypointBundlePath(
1265
- entry,
1266
- config.outDir,
1267
- entry.inputPath.endsWith(".html") ? ".html" : ".js"
1268
- );
1269
- return ` | "/${path5}"`;
1270
- }).sort()
1271
- ].join("\n") + "\n"
1272
- );
1273
- return filePath;
1274
- }
1275
- async function writeGlobalsDeclarationFile(config) {
1276
- const filePath = (0, import_path10.resolve)(config.typesDir, "globals.d.ts");
1277
- const globals = getGlobals(config);
1278
- await import_fs_extra9.default.writeFile(
1279
- filePath,
1280
- [
1281
- "// Generated by wxt",
1282
- "export {}",
1283
- "declare global {",
1284
- ...globals.map((global) => ` const ${global.name}: ${global.type};`),
1285
- "}"
1286
- ].join("\n") + "\n",
1287
- "utf-8"
1288
- );
1289
- return filePath;
1290
- }
1291
- async function writeMainDeclarationFile(references, config) {
1292
- const dir = config.wxtDir;
1293
- const filePath = (0, import_path10.resolve)(dir, "wxt.d.ts");
1294
- await import_fs_extra9.default.writeFile(
1295
- filePath,
1296
- [
1297
- "// Generated by wxt",
1298
- ...references.map(
1299
- (ref) => `/// <reference types="./${(0, import_path10.relative)(dir, ref)}" />`
1300
- )
1301
- ].join("\n") + "\n"
1506
+ const startTime = Date.now();
1507
+ await import_fs_extra11.default.rm(config.outDir, { recursive: true, force: true });
1508
+ await import_fs_extra11.default.ensureDir(config.outDir);
1509
+ const entrypoints = await findEntrypoints(config);
1510
+ const groups = groupEntrypoints(entrypoints);
1511
+ const { output } = await rebuild(config, groups);
1512
+ config.logger.success(
1513
+ `Built extension in ${formatDuration(Date.now() - startTime)}`
1302
1514
  );
1303
- return filePath;
1515
+ await printBuildSummary(output, config);
1516
+ return output;
1304
1517
  }
1305
- async function writeTsConfigFile(mainReference, config) {
1306
- const dir = config.wxtDir;
1307
- await import_fs_extra9.default.writeFile(
1308
- (0, import_path10.resolve)(dir, "tsconfig.json"),
1309
- `{
1310
- "compilerOptions": {
1311
- "target": "ESNext",
1312
- "module": "ESNext",
1313
- "moduleResolution": "Bundler",
1314
- "noEmit": true,
1315
- "esModuleInterop": true,
1316
- "forceConsistentCasingInFileNames": true,
1317
- "resolveJsonModule": true,
1318
-
1319
- /* Type Checking */
1320
- "strict": true,
1321
-
1322
- /* Completeness */
1323
- "skipLibCheck": true
1324
- },
1325
- "include": [
1326
- "${(0, import_path10.relative)(dir, config.root)}/**/*",
1327
- "./${(0, import_path10.relative)(dir, mainReference)}"
1328
- ],
1329
- "exclude": ["${(0, import_path10.relative)(dir, config.outBaseDir)}"]
1330
- }`
1518
+ async function rebuild(config, entrypointGroups, existingOutput = {
1519
+ steps: [],
1520
+ publicAssets: []
1521
+ }) {
1522
+ const allEntrypoints = await findEntrypoints(config);
1523
+ await generateTypesDir(allEntrypoints, config);
1524
+ const newOutput = await buildEntrypoints(entrypointGroups, config);
1525
+ const mergedOutput = {
1526
+ steps: [...existingOutput.steps, ...newOutput.steps],
1527
+ publicAssets: [...existingOutput.publicAssets, ...newOutput.publicAssets]
1528
+ };
1529
+ const newManifest = await generateMainfest(
1530
+ allEntrypoints,
1531
+ mergedOutput,
1532
+ config
1331
1533
  );
1534
+ const finalOutput = {
1535
+ manifest: newManifest,
1536
+ ...newOutput
1537
+ };
1538
+ await writeManifest(newManifest, finalOutput, config);
1539
+ return {
1540
+ output: {
1541
+ manifest: newManifest,
1542
+ steps: [...existingOutput.steps, ...finalOutput.steps],
1543
+ publicAssets: [
1544
+ ...existingOutput.publicAssets,
1545
+ ...finalOutput.publicAssets
1546
+ ]
1547
+ },
1548
+ manifest: newManifest
1549
+ };
1332
1550
  }
1333
1551
 
1334
- // src/index.ts
1335
- var import_picocolors2 = __toESM(require("picocolors"), 1);
1336
- var vite4 = __toESM(require("vite"), 1);
1552
+ // src/core/server.ts
1553
+ var vite5 = __toESM(require("vite"), 1);
1337
1554
 
1338
1555
  // src/core/utils/findOpenPort.ts
1339
1556
  var import_node_net = __toESM(require("net"), 1);
@@ -1358,17 +1575,6 @@ function findOpenPortRecursive(port, startPort, endPort) {
1358
1575
  });
1359
1576
  }
1360
1577
 
1361
- // src/core/utils/formatDuration.ts
1362
- function formatDuration(duration) {
1363
- if (duration < 1e3)
1364
- return `${duration} ms`;
1365
- if (duration < 1e4)
1366
- return `${(duration / 1e3).toFixed(3)} s`;
1367
- if (duration < 6e4)
1368
- return `${(duration / 1e3).toFixed(1)} s`;
1369
- return `${(duration / 1e3).toFixed(0)} s`;
1370
- }
1371
-
1372
1578
  // src/core/runners/createWebExtRunner.ts
1373
1579
  function createWebExtRunner() {
1374
1580
  let runner;
@@ -1426,120 +1632,93 @@ function createWebExtRunner() {
1426
1632
  var WARN_LOG_LEVEL = 40;
1427
1633
  var ERROR_LOG_LEVEL = 50;
1428
1634
 
1429
- // src/core/utils/groupEntrypoints.ts
1430
- function groupEntrypoints(entrypoints) {
1431
- const groupIndexMap = {};
1432
- const groups = [];
1433
- for (const entry of entrypoints) {
1434
- const group = ENTRY_TYPE_TO_GROUP_MAP[entry.type];
1435
- if (group === "no-group") {
1436
- groups.push(entry);
1437
- } else {
1438
- let groupIndex = groupIndexMap[group];
1439
- if (groupIndex == null) {
1440
- groupIndex = groups.push([]) - 1;
1441
- groupIndexMap[group] = groupIndex;
1442
- }
1443
- groups[groupIndex].push(entry);
1635
+ // src/core/server.ts
1636
+ async function getServerInfo() {
1637
+ const port = await findOpenPort(3e3, 3010);
1638
+ const hostname = "localhost";
1639
+ const origin = `http://${hostname}:${port}`;
1640
+ const serverConfig = {
1641
+ server: {
1642
+ origin
1444
1643
  }
1445
- }
1446
- return groups;
1644
+ };
1645
+ return {
1646
+ port,
1647
+ hostname,
1648
+ origin,
1649
+ viteServerConfig: serverConfig
1650
+ };
1447
1651
  }
1448
- var ENTRY_TYPE_TO_GROUP_MAP = {
1449
- sandbox: "sandbox-page",
1450
- popup: "extension-page",
1451
- newtab: "extension-page",
1452
- history: "extension-page",
1453
- options: "extension-page",
1454
- devtools: "extension-page",
1455
- bookmarks: "extension-page",
1456
- sidepanel: "extension-page",
1457
- "unlisted-page": "extension-page",
1458
- background: "no-group",
1459
- "content-script": "no-group",
1460
- "unlisted-script": "no-group"
1461
- };
1462
-
1463
- // src/core/utils/detectDevChanges.ts
1464
- function detectDevChanges(changedFiles, currentOutput) {
1465
- if (currentOutput == null)
1466
- return { type: "no-change" };
1467
- const changedSteps = new Set(
1468
- changedFiles.flatMap(
1469
- (changedFile) => findEffectedSteps(changedFile, currentOutput)
1470
- )
1652
+ async function setupServer(serverInfo, config) {
1653
+ const runner = createWebExtRunner();
1654
+ const viteServer = await vite5.createServer(
1655
+ vite5.mergeConfig(serverInfo, config.vite)
1471
1656
  );
1472
- if (changedSteps.size === 0)
1473
- return { type: "no-change" };
1474
- const unchangedOutput = {
1475
- manifest: currentOutput.manifest,
1476
- steps: [],
1477
- publicAssets: []
1657
+ const start = async () => {
1658
+ await viteServer.listen(server.port);
1659
+ config.logger.success(`Started dev server @ ${serverInfo.origin}`);
1660
+ server.currentOutput = await buildInternal(config);
1661
+ config.logger.info("Opening browser...");
1662
+ await runner.openBrowser(config);
1663
+ config.logger.success("Opened!");
1478
1664
  };
1479
- const changedOutput = {
1480
- manifest: currentOutput.manifest,
1481
- steps: [],
1482
- publicAssets: []
1665
+ const reloadExtension = () => {
1666
+ viteServer.ws.send("wxt:reload-extension");
1483
1667
  };
1484
- for (const step of currentOutput.steps) {
1485
- if (changedSteps.has(step)) {
1486
- changedOutput.steps.push(step);
1487
- } else {
1488
- unchangedOutput.steps.push(step);
1489
- }
1490
- }
1491
- for (const asset of currentOutput.publicAssets) {
1492
- if (changedSteps.has(asset)) {
1493
- changedOutput.publicAssets.push(asset);
1494
- } else {
1495
- unchangedOutput.publicAssets.push(asset);
1496
- }
1497
- }
1498
- const isOnlyHtmlChanges = !changedFiles.find(
1499
- ([_, file]) => !file.endsWith(".html")
1500
- );
1501
- if (isOnlyHtmlChanges) {
1502
- return {
1503
- type: "html-reload",
1504
- cachedOutput: unchangedOutput,
1505
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
1506
- };
1507
- }
1508
- return {
1509
- type: "extension-reload",
1510
- cachedOutput: unchangedOutput,
1511
- rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
1668
+ const reloadPage = (path5) => {
1669
+ viteServer.ws.send("wxt:reload-page", path5);
1670
+ };
1671
+ const reloadContentScript = (contentScript) => {
1672
+ viteServer.ws.send("wxt:reload-content-script", contentScript);
1673
+ };
1674
+ const server = {
1675
+ ...viteServer,
1676
+ start,
1677
+ currentOutput: {
1678
+ manifest: {
1679
+ manifest_version: 3,
1680
+ name: "",
1681
+ version: ""
1682
+ },
1683
+ publicAssets: [],
1684
+ steps: []
1685
+ },
1686
+ port: serverInfo.port,
1687
+ hostname: serverInfo.hostname,
1688
+ origin: serverInfo.origin,
1689
+ reloadExtension,
1690
+ reloadPage,
1691
+ reloadContentScript
1512
1692
  };
1693
+ return server;
1513
1694
  }
1514
- function findEffectedSteps(changedFile, currentOutput) {
1515
- const changes = [];
1516
- const changedPath = changedFile[1];
1517
- const isChunkEffected = (chunk) => (
1518
- // If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
1519
- // TODO: use bundle path to support `<name>/index.html`?
1520
- chunk.type === "asset" && changedPath.endsWith(chunk.fileName) || // If it's a chunk that depends on the changed file, it is effected
1521
- chunk.type === "chunk" && chunk.moduleIds.includes(changedPath)
1522
- );
1523
- for (const step of currentOutput.steps) {
1524
- const effectedChunk = step.chunks.find((chunk) => isChunkEffected(chunk));
1525
- if (effectedChunk)
1526
- changes.push(step);
1695
+ function reloadContentScripts(steps, config, server) {
1696
+ if (config.manifestVersion === 3) {
1697
+ steps.forEach((step) => {
1698
+ const entry = step.entrypoints;
1699
+ if (Array.isArray(entry) || entry.type !== "content-script")
1700
+ return;
1701
+ const js = [getEntrypointBundlePath(entry, config.outDir, ".js")];
1702
+ const css = getContentScriptCssFiles([entry], server.currentOutput);
1703
+ server.reloadContentScript({
1704
+ js,
1705
+ css,
1706
+ ...entry.options
1707
+ });
1708
+ });
1709
+ } else {
1710
+ server.reloadExtension();
1527
1711
  }
1528
- const effectedAsset = currentOutput.publicAssets.find(
1529
- (chunk) => isChunkEffected(chunk)
1530
- );
1531
- if (effectedAsset)
1532
- changes.push(effectedAsset);
1533
- return changes;
1534
1712
  }
1535
-
1536
- // src/index.ts
1537
- var import_async_mutex = require("async-mutex");
1538
- var import_consola3 = require("consola");
1539
- var import_node_path4 = require("path");
1713
+ function reloadHtmlPages(groups, server, config) {
1714
+ groups.flat().forEach((entry) => {
1715
+ const path5 = getEntrypointBundlePath(entry, config.outDir, ".html");
1716
+ server.reloadPage(path5);
1717
+ });
1718
+ }
1540
1719
 
1541
1720
  // package.json
1542
- var version = "0.0.2";
1721
+ var version2 = "0.1.1-alpha1";
1543
1722
 
1544
1723
  // src/core/utils/defineConfig.ts
1545
1724
  function defineConfig(config) {
@@ -1557,45 +1736,40 @@ async function build2(config) {
1557
1736
  return await buildInternal(internalConfig);
1558
1737
  }
1559
1738
  async function createServer2(config) {
1560
- const port = await findOpenPort(3e3, 3010);
1561
- const hostname = "localhost";
1562
- const origin = `http://${hostname}:${port}`;
1563
- const serverConfig = {
1564
- server: {
1565
- origin
1566
- }
1739
+ const serverInfo = await getServerInfo();
1740
+ const getLatestInternalConfig = () => {
1741
+ const viteConfig = vite6.mergeConfig(
1742
+ serverInfo.viteServerConfig,
1743
+ config?.vite ?? {}
1744
+ );
1745
+ return getInternalConfig({ ...config, vite: viteConfig }, "serve");
1567
1746
  };
1568
- let internalConfig = await getInternalConfig(
1569
- vite4.mergeConfig(serverConfig, config ?? {}),
1570
- "serve"
1571
- );
1572
- const runner = createWebExtRunner();
1573
- let hasBuiltOnce = false;
1574
- let currentOutput;
1747
+ let internalConfig = await getLatestInternalConfig();
1748
+ const server = await setupServer(serverInfo, internalConfig);
1749
+ internalConfig.server = server;
1575
1750
  const fileChangedMutex = new import_async_mutex.Mutex();
1576
1751
  const changeQueue = [];
1577
- const viteServer = await vite4.createServer(internalConfig.vite);
1578
- viteServer.watcher.on("all", async (event, path5, stats) => {
1579
- if (!hasBuiltOnce || path5.startsWith(internalConfig.outBaseDir))
1752
+ server.ws.on("wxt:background-initialized", () => {
1753
+ reloadContentScripts(server.currentOutput.steps, internalConfig, server);
1754
+ });
1755
+ server.watcher.on("all", async (event, path5, _stats) => {
1756
+ if (path5.startsWith(internalConfig.outBaseDir))
1580
1757
  return;
1581
1758
  changeQueue.push([event, path5]);
1582
1759
  await fileChangedMutex.runExclusive(async () => {
1583
1760
  const fileChanges = changeQueue.splice(0, changeQueue.length);
1584
- const changes = detectDevChanges(fileChanges, currentOutput);
1761
+ const changes = detectDevChanges(fileChanges, server.currentOutput);
1585
1762
  if (changes.type === "no-change")
1586
1763
  return;
1587
1764
  import_consola3.consola.info(
1588
- `Changed: ${Array.from(new Set(fileChanges.map((change) => change[1]))).map((file) => import_picocolors2.default.dim((0, import_node_path4.relative)(internalConfig.root, file))).join(", ")}`
1765
+ `Changed: ${Array.from(new Set(fileChanges.map((change) => change[1]))).map((file) => import_picocolors3.default.dim((0, import_node_path4.relative)(internalConfig.root, file))).join(", ")}`
1589
1766
  );
1590
1767
  const rebuiltNames = changes.rebuildGroups.flat().map((entry) => {
1591
- return import_picocolors2.default.cyan(
1768
+ return import_picocolors3.default.cyan(
1592
1769
  (0, import_node_path4.relative)(internalConfig.outDir, getEntrypointOutputFile(entry, ""))
1593
1770
  );
1594
- }).join(import_picocolors2.default.dim(", "));
1595
- internalConfig = await getInternalConfig(
1596
- vite4.mergeConfig(serverConfig, config ?? {}),
1597
- "serve"
1598
- );
1771
+ }).join(import_picocolors3.default.dim(", "));
1772
+ internalConfig = await getLatestInternalConfig();
1599
1773
  internalConfig.server = server;
1600
1774
  const { output: newOutput } = await rebuild(
1601
1775
  internalConfig,
@@ -1603,103 +1777,29 @@ async function createServer2(config) {
1603
1777
  changes.rebuildGroups,
1604
1778
  changes.cachedOutput
1605
1779
  );
1606
- currentOutput = newOutput;
1780
+ server.currentOutput = newOutput;
1607
1781
  switch (changes.type) {
1608
1782
  case "extension-reload":
1609
1783
  server.reloadExtension();
1610
- import_consola3.consola.success(`Reloaded extension: ${rebuiltNames}`);
1611
1784
  break;
1612
1785
  case "html-reload":
1613
- changes.rebuildGroups.flat().forEach((entry) => {
1614
- const path6 = getEntrypointBundlePath(
1615
- entry,
1616
- internalConfig.outDir,
1617
- ".html"
1618
- );
1619
- server.reloadPage(path6);
1620
- });
1621
- import_consola3.consola.success(`Reloaded pages: ${rebuiltNames}`);
1786
+ reloadHtmlPages(changes.rebuildGroups, server, internalConfig);
1787
+ break;
1788
+ case "content-script-reload":
1789
+ reloadContentScripts(changes.changedSteps, internalConfig, server);
1622
1790
  break;
1623
1791
  }
1792
+ import_consola3.consola.success(`Reloaded: ${rebuiltNames}`);
1624
1793
  });
1625
1794
  });
1626
- const server = {
1627
- ...viteServer,
1628
- async listen(port2, isRestart) {
1629
- const res = await viteServer.listen(port2, isRestart);
1630
- if (!isRestart) {
1631
- internalConfig.logger.success(`Started dev server @ ${origin}`);
1632
- internalConfig.logger.info("Opening browser...");
1633
- await runner.openBrowser(internalConfig);
1634
- internalConfig.logger.success("Opened!");
1635
- }
1636
- return res;
1637
- },
1638
- port,
1639
- hostname,
1640
- origin,
1641
- reloadExtension: () => {
1642
- server.ws.send("wxt:reload-extension");
1643
- },
1644
- reloadPage: (path5) => {
1645
- server.ws.send("wxt:reload-page", path5);
1646
- }
1647
- };
1648
- internalConfig.logger.info("Created dev server");
1649
- internalConfig.server = server;
1650
- currentOutput = await buildInternal(internalConfig);
1651
- hasBuiltOnce = true;
1652
1795
  return server;
1653
1796
  }
1654
- async function buildInternal(config) {
1655
- const verb = config.command === "serve" ? "Pre-rendering" : "Building";
1656
- const target = `${config.browser}-mv${config.manifestVersion}`;
1657
- config.logger.info(
1658
- `${verb} ${import_picocolors2.default.cyan(target)} for ${import_picocolors2.default.cyan(config.mode)} with ${import_picocolors2.default.green(
1659
- `Vite ${vite4.version}`
1660
- )}`
1661
- );
1662
- const startTime = Date.now();
1663
- await import_fs_extra10.default.rm(config.outDir, { recursive: true, force: true });
1664
- await import_fs_extra10.default.ensureDir(config.outDir);
1665
- const entrypoints = await findEntrypoints(config);
1666
- const groups = groupEntrypoints(entrypoints);
1667
- const { output } = await rebuild(config, groups);
1668
- config.logger.success(
1669
- `Built extension in ${formatDuration(Date.now() - startTime)}`
1670
- );
1671
- await printBuildSummary(output, config);
1672
- return output;
1673
- }
1674
- async function rebuild(config, entrypointGroups, existingOutput = {
1675
- steps: [],
1676
- publicAssets: []
1677
- }) {
1678
- const allEntrypoints = await findEntrypoints(config);
1679
- await generateTypesDir(allEntrypoints, config);
1680
- const buildOutput = await buildEntrypoints(entrypointGroups, config);
1681
- const manifest = await generateMainfest(allEntrypoints, buildOutput, config);
1682
- const output = {
1683
- manifest,
1684
- ...buildOutput
1685
- };
1686
- await writeManifest(manifest, output, config);
1687
- return {
1688
- output: {
1689
- manifest,
1690
- steps: [...existingOutput.steps, ...output.steps],
1691
- publicAssets: [...existingOutput.publicAssets, ...output.publicAssets]
1692
- },
1693
- manifest
1694
- };
1695
- }
1696
1797
  // Annotate the CommonJS export names for ESM import in node:
1697
1798
  0 && (module.exports = {
1698
1799
  build,
1699
1800
  createServer,
1700
1801
  defineConfig,
1701
1802
  defineRunnerConfig,
1702
- rebuild,
1703
1803
  version
1704
1804
  });
1705
1805
  //# sourceMappingURL=index.cjs.map