wxt 0.21.0 → 0.21.2

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.
Files changed (153) hide show
  1. package/README.md +0 -1
  2. package/bin/wxt-publish-extension.mjs +4 -3
  3. package/dist/browser.d.mts +7 -6
  4. package/dist/browser.mjs +6 -5
  5. package/dist/builtin-modules/favicon-permission.mjs +28 -0
  6. package/dist/builtin-modules/index.mjs +3 -4
  7. package/dist/builtin-modules/unimport.mjs +1 -3
  8. package/dist/cli/cli-utils.mjs +8 -9
  9. package/dist/cli/commands.mjs +1 -3
  10. package/dist/cli/index.mjs +1 -3
  11. package/dist/core/build.d.mts +9 -8
  12. package/dist/core/build.mjs +10 -11
  13. package/dist/core/builders/vite/index.mjs +123 -75
  14. package/dist/core/builders/vite/plugins/bundleAnalysis.mjs +1 -3
  15. package/dist/core/builders/vite/plugins/cssEntrypoints.mjs +6 -6
  16. package/dist/core/builders/vite/plugins/defineImportMeta.mjs +3 -4
  17. package/dist/core/builders/vite/plugins/devHtmlPrerender.mjs +34 -26
  18. package/dist/core/builders/vite/plugins/devServerGlobals.mjs +3 -3
  19. package/dist/core/builders/vite/plugins/entrypointGroupGlobals.mjs +2 -6
  20. package/dist/core/builders/vite/plugins/extensionApiMock.mjs +13 -8
  21. package/dist/core/builders/vite/plugins/globals.mjs +1 -3
  22. package/dist/core/builders/vite/plugins/iifeAnonymous.mjs +1 -2
  23. package/dist/core/builders/vite/plugins/iifeFooter.mjs +14 -7
  24. package/dist/core/builders/vite/plugins/index.mjs +17 -19
  25. package/dist/core/builders/vite/plugins/noopBackground.mjs +14 -9
  26. package/dist/core/builders/vite/plugins/removeEntrypointMainFunction.mjs +10 -12
  27. package/dist/core/builders/vite/plugins/resolveAppConfig.mjs +14 -10
  28. package/dist/core/builders/vite/plugins/resolveVirtualModules.mjs +15 -13
  29. package/dist/core/builders/vite/plugins/tsconfigPaths.mjs +1 -2
  30. package/dist/core/builders/vite/plugins/wxtPluginLoader.mjs +22 -17
  31. package/dist/core/clean.d.mts +3 -14
  32. package/dist/core/clean.mjs +19 -13
  33. package/dist/core/create-server.d.mts +6 -5
  34. package/dist/core/create-server.mjs +21 -117
  35. package/dist/core/define-config.mjs +1 -2
  36. package/dist/core/define-web-ext-config.d.mts +3 -6
  37. package/dist/core/define-web-ext-config.mjs +3 -12
  38. package/dist/core/generate-wxt-dir.mjs +53 -35
  39. package/dist/core/index.d.mts +3 -2
  40. package/dist/core/index.mjs +11 -11
  41. package/dist/core/initialize.mjs +28 -29
  42. package/dist/core/keyboard-shortcuts.mjs +4 -8
  43. package/dist/core/package-managers/bun.mjs +1 -3
  44. package/dist/core/package-managers/deno.mjs +1 -2
  45. package/dist/core/package-managers/index.mjs +1 -3
  46. package/dist/core/package-managers/npm.mjs +3 -5
  47. package/dist/core/package-managers/pnpm.mjs +1 -3
  48. package/dist/core/package-managers/yarn.mjs +1 -3
  49. package/dist/core/prepare.mjs +1 -3
  50. package/dist/core/resolve-config.mjs +63 -43
  51. package/dist/core/runners/manual.mjs +2 -6
  52. package/dist/core/runners/safari.mjs +3 -4
  53. package/dist/core/runners/web-ext.mjs +8 -11
  54. package/dist/core/runners/wsl.mjs +3 -4
  55. package/dist/core/utils/arrays.mjs +4 -11
  56. package/dist/core/utils/building/build-entrypoints.mjs +10 -11
  57. package/dist/core/utils/building/detect-dev-changes.mjs +37 -19
  58. package/dist/core/utils/building/find-entrypoints.mjs +40 -21
  59. package/dist/core/utils/building/group-entrypoints.mjs +3 -4
  60. package/dist/core/utils/building/index.mjs +7 -8
  61. package/dist/core/utils/building/internal-build.mjs +21 -21
  62. package/dist/core/utils/building/rebuild.mjs +10 -10
  63. package/dist/core/utils/cache.mjs +6 -8
  64. package/dist/core/utils/constants.mjs +3 -4
  65. package/dist/core/utils/content-scripts.mjs +3 -4
  66. package/dist/core/utils/content-security-policy.mjs +2 -5
  67. package/dist/core/utils/create-file-reloader.mjs +158 -0
  68. package/dist/core/utils/entrypoints.d.mts +10 -0
  69. package/dist/core/utils/entrypoints.mjs +6 -10
  70. package/dist/core/utils/env.mjs +24 -19
  71. package/dist/core/utils/environments/browser-environment.mjs +1 -3
  72. package/dist/core/utils/environments/environment.mjs +1 -2
  73. package/dist/core/utils/environments/extension-environment.mjs +1 -3
  74. package/dist/core/utils/environments/index.mjs +3 -4
  75. package/dist/core/utils/eslint.mjs +1 -2
  76. package/dist/core/utils/fs.mjs +23 -14
  77. package/dist/core/utils/globals.mjs +1 -2
  78. package/dist/core/utils/i18n.mjs +2 -3
  79. package/dist/core/utils/index.d.mts +2 -0
  80. package/dist/core/utils/index.mjs +3 -3
  81. package/dist/core/utils/log/index.mjs +5 -6
  82. package/dist/core/utils/log/printBuildSummary.mjs +1 -3
  83. package/dist/core/utils/log/printFileList.mjs +25 -21
  84. package/dist/core/utils/log/printHeader.mjs +3 -5
  85. package/dist/core/utils/log/printTable.mjs +1 -2
  86. package/dist/core/utils/manifest.mjs +39 -46
  87. package/dist/core/utils/number.mjs +1 -2
  88. package/dist/core/utils/package.mjs +3 -5
  89. package/dist/core/utils/paths.d.mts +2 -2
  90. package/dist/core/utils/paths.mjs +7 -9
  91. package/dist/core/utils/strings.mjs +4 -7
  92. package/dist/core/utils/syntax-errors.mjs +3 -5
  93. package/dist/core/utils/theme-icons.mjs +71 -0
  94. package/dist/core/utils/time.mjs +1 -13
  95. package/dist/core/utils/transform.mjs +9 -7
  96. package/dist/core/utils/validation.mjs +1 -2
  97. package/dist/core/utils/virtual-modules.mjs +9 -15
  98. package/dist/core/wxt.mjs +11 -12
  99. package/dist/core/zip.d.mts +1 -0
  100. package/dist/core/zip.mjs +25 -20
  101. package/dist/index.d.mts +4 -4
  102. package/dist/index.mjs +3 -3
  103. package/dist/modules.d.mts +64 -61
  104. package/dist/modules.mjs +70 -66
  105. package/dist/testing/fake-browser.mjs +1 -2
  106. package/dist/testing/wxt-vitest-plugin.d.mts +4 -2
  107. package/dist/testing/wxt-vitest-plugin.mjs +5 -7
  108. package/dist/types.d.mts +636 -450
  109. package/dist/utils/app-config.d.mts +1 -0
  110. package/dist/utils/app-config.mjs +3 -3
  111. package/dist/utils/content-script-context.d.mts +49 -39
  112. package/dist/utils/content-script-context.mjs +37 -32
  113. package/dist/utils/content-script-ui/iframe.d.mts +14 -15
  114. package/dist/utils/content-script-ui/iframe.mjs +1 -3
  115. package/dist/utils/content-script-ui/integrated.d.mts +8 -7
  116. package/dist/utils/content-script-ui/integrated.mjs +1 -3
  117. package/dist/utils/content-script-ui/shadow-root.d.mts +37 -25
  118. package/dist/utils/content-script-ui/shadow-root.mjs +5 -8
  119. package/dist/utils/content-script-ui/shared.mjs +1 -3
  120. package/dist/utils/content-script-ui/types.d.mts +29 -34
  121. package/dist/utils/content-script-ui/types.mjs +1 -1
  122. package/dist/utils/define-app-config.d.mts +4 -3
  123. package/dist/utils/define-app-config.mjs +5 -5
  124. package/dist/utils/define-background.mjs +1 -2
  125. package/dist/utils/define-content-script.mjs +1 -2
  126. package/dist/utils/define-unlisted-script.mjs +1 -2
  127. package/dist/utils/define-wxt-plugin.mjs +1 -2
  128. package/dist/utils/inject-script.d.mts +2 -2
  129. package/dist/utils/inject-script.mjs +4 -5
  130. package/dist/utils/internal/custom-events.mjs +3 -4
  131. package/dist/utils/internal/location-watcher.mjs +4 -6
  132. package/dist/utils/internal/logger.mjs +2 -5
  133. package/dist/utils/match-patterns.mjs +2 -3
  134. package/dist/utils/split-shadow-root-css.d.mts +6 -2
  135. package/dist/utils/split-shadow-root-css.mjs +7 -4
  136. package/dist/utils/storage.mjs +2 -3
  137. package/dist/version.mjs +2 -3
  138. package/dist/virtual/background-entrypoint.mjs +4 -16
  139. package/dist/virtual/content-script-isolated-world-entrypoint.mjs +2 -7
  140. package/dist/virtual/content-script-main-world-entrypoint.mjs +2 -7
  141. package/dist/virtual/mock-browser.d.mts +1 -1
  142. package/dist/virtual/mock-browser.mjs +2 -4
  143. package/dist/virtual/reload-html.mjs +3 -10
  144. package/dist/virtual/unlisted-script-entrypoint.mjs +2 -7
  145. package/package.json +55 -49
  146. package/LICENSE +0 -21
  147. package/dist/core/builders/vite/plugins/download.mjs +0 -25
  148. package/dist/core/runners/index.mjs +0 -17
  149. package/dist/core/utils/minimatch-multiple.mjs +0 -28
  150. package/dist/core/utils/network.mjs +0 -38
  151. package/dist/core/utils/wsl.mjs +0 -12
  152. package/dist/testing/index.d.mts +0 -3
  153. package/dist/testing/index.mjs +0 -4
@@ -2,33 +2,36 @@ import { CSS_EXTENSIONS_PATTERN } from "../paths.mjs";
2
2
  import { getEntrypointName, isHtmlEntrypoint, isJsEntrypoint, resolvePerBrowserOptions } from "../entrypoints.mjs";
3
3
  import { VIRTUAL_NOOP_BACKGROUND_MODULE_ID } from "../constants.mjs";
4
4
  import { wxt } from "../../wxt.mjs";
5
- import fs from "fs-extra";
6
- import glob from "fast-glob";
5
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
6
+ import { glob } from "tinyglobby";
7
7
  import { relative, resolve } from "path";
8
8
  import { parseHTML } from "linkedom";
9
9
  import { camelCase } from "scule";
10
- import pc from "picocolors";
11
- import { minimatch } from "minimatch";
10
+ import { styleText } from "node:util";
11
+ import picomatch from "picomatch";
12
12
  import JSON5 from "json5";
13
-
14
13
  //#region src/core/utils/building/find-entrypoints.ts
15
14
  /**
16
- * Return entrypoints and their configuration by looking through the project's files.
15
+ * Return entrypoints and their configuration by looking through the project's
16
+ * files.
17
17
  */
18
18
  async function findEntrypoints() {
19
- await fs.mkdir(wxt.config.wxtDir, { recursive: true });
19
+ await mkdir(wxt.config.wxtDir, { recursive: true });
20
20
  try {
21
- await fs.writeJson(resolve(wxt.config.wxtDir, "tsconfig.json"), {}, { flag: "wx" });
21
+ await writeFile(resolve(wxt.config.wxtDir, "tsconfig.json"), JSON.stringify({}), { flag: "wx" });
22
22
  } catch (err) {
23
23
  if (!(err instanceof Error) || !("code" in err) || err.code !== "EEXIST") throw err;
24
24
  }
25
- const relativePaths = await glob(Object.keys(PATH_GLOB_TO_TYPE_MAP), { cwd: wxt.config.entrypointsDir });
25
+ const relativePaths = await glob(Object.keys(PATH_GLOB_TO_TYPE_MAP), {
26
+ cwd: wxt.config.entrypointsDir,
27
+ expandDirectories: false
28
+ });
26
29
  relativePaths.sort();
27
30
  const pathGlobs = Object.keys(PATH_GLOB_TO_TYPE_MAP);
28
31
  const entrypointInfos = relativePaths.reduce((results, relativePath) => {
29
32
  const inputPath = resolve(wxt.config.entrypointsDir, relativePath);
30
33
  const name = getEntrypointName(wxt.config.entrypointsDir, inputPath);
31
- const matchingGlob = pathGlobs.find((glob) => minimatch(relativePath, glob));
34
+ const matchingGlob = pathGlobs.find((glob) => picomatch(glob)(relativePath));
32
35
  if (matchingGlob) {
33
36
  const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob];
34
37
  results.push({
@@ -87,7 +90,7 @@ async function findEntrypoints() {
87
90
  await wxt.hooks.callHook("entrypoints:resolved", wxt, entrypoints);
88
91
  wxt.logger.debug("All entrypoints:", entrypoints);
89
92
  const skippedEntrypointNames = entrypoints.filter((item) => item.skipped).map((item) => item.name);
90
- if (skippedEntrypointNames.length) wxt.logger.warn(["The following entrypoints have been skipped:", ...skippedEntrypointNames.map((item) => `${pc.dim("-")} ${pc.cyan(item)}`)].join("\n"));
93
+ if (skippedEntrypointNames.length) wxt.logger.warn(["The following entrypoints have been skipped:", ...skippedEntrypointNames.map((item) => `${styleText("dim", "-")} ${styleText("cyan", item)}`)].join("\n"));
91
94
  return entrypoints;
92
95
  }
93
96
  /** Returns a map of input paths to the file's options. */
@@ -104,9 +107,12 @@ async function importEntrypoints(infos) {
104
107
  })()]);
105
108
  return resMap;
106
109
  }
107
- /** Extract `manifest.` and `wxt.` options from meta tags, converting snake_case keys to camelCase */
110
+ /**
111
+ * Extract `manifest.` and `wxt.` options from meta tags, converting snake_case
112
+ * keys to camelCase
113
+ */
108
114
  async function importHtmlEntrypoint(info) {
109
- const { document } = parseHTML(await fs.readFile(info.inputPath, "utf-8"));
115
+ const { document } = parseHTML(await readFile(info.inputPath, "utf-8"));
110
116
  const metaTags = document.querySelectorAll("meta");
111
117
  const res = { title: document.querySelector("title")?.textContent || void 0 };
112
118
  metaTags.forEach((tag) => {
@@ -151,16 +157,30 @@ async function getPopupEntrypoint(info, options) {
151
157
  const strictOptions = resolvePerBrowserOptions({
152
158
  ...perBrowserOptions,
153
159
  defaultTitle: title,
154
- mv2Key: type
160
+ actionType: type
155
161
  }, wxt.config.browser);
156
- if (strictOptions.mv2Key && strictOptions.mv2Key !== "page_action") strictOptions.mv2Key = "browser_action";
162
+ if (strictOptions.actionType && strictOptions.actionType !== "page_action") strictOptions.actionType = "browser_action";
163
+ const opts = {
164
+ ...strictOptions,
165
+ themeIcons
166
+ };
167
+ let _actionType = opts.actionType;
168
+ Object.defineProperty(opts, "actionType", {
169
+ get: () => _actionType,
170
+ set: (v) => _actionType = v,
171
+ enumerable: true,
172
+ configurable: true
173
+ });
174
+ Object.defineProperty(opts, "mv2Key", {
175
+ get: () => _actionType,
176
+ set: (v) => _actionType = v,
177
+ enumerable: true,
178
+ configurable: true
179
+ });
157
180
  return {
158
181
  type: "popup",
159
182
  name: "popup",
160
- options: {
161
- ...strictOptions,
162
- themeIcons
163
- },
183
+ options: opts,
164
184
  inputPath: info.inputPath,
165
185
  outputDir: wxt.config.outDir
166
186
  };
@@ -276,6 +296,5 @@ const PATH_GLOB_TO_TYPE_MAP = {
276
296
  [`*/index.${CSS_EXTENSIONS_PATTERN}`]: "unlisted-style"
277
297
  };
278
298
  const CONTENT_SCRIPT_OUT_DIR = "content-scripts";
279
-
280
299
  //#endregion
281
- export { findEntrypoints };
300
+ export { findEntrypoints };
@@ -1,7 +1,7 @@
1
1
  //#region src/core/utils/building/group-entrypoints.ts
2
2
  /**
3
- * Entrypoints are built in groups. HTML pages can all be built together in a single step,
4
- * content scripts must be build individually, etc.
3
+ * Entrypoints are built in groups. HTML pages can all be built together in a
4
+ * single step, content scripts must be build individually, etc.
5
5
  *
6
6
  * This function returns the entrypoints put into these types of groups.
7
7
  */
@@ -40,6 +40,5 @@ const ENTRY_TYPE_TO_GROUP_MAP = {
40
40
  "unlisted-style": "individual",
41
41
  "content-script-style": "individual"
42
42
  };
43
-
44
43
  //#endregion
45
- export { groupEntrypoints };
44
+ export { groupEntrypoints };
@@ -1,8 +1,7 @@
1
- import { buildEntrypoints } from "./build-entrypoints.mjs";
2
- import { detectDevChanges } from "./detect-dev-changes.mjs";
3
- import { findEntrypoints } from "./find-entrypoints.mjs";
4
- import { groupEntrypoints } from "./group-entrypoints.mjs";
5
- import { rebuild } from "./rebuild.mjs";
6
- import { internalBuild } from "./internal-build.mjs";
7
-
8
- export { };
1
+ import "./build-entrypoints.mjs";
2
+ import "./detect-dev-changes.mjs";
3
+ import "./find-entrypoints.mjs";
4
+ import "./group-entrypoints.mjs";
5
+ import "./rebuild.mjs";
6
+ import "./internal-build.mjs";
7
+ export {};
@@ -1,41 +1,41 @@
1
1
  import { unnormalizePath } from "../paths.mjs";
2
- import { formatDuration } from "../time.mjs";
3
2
  import { wxt } from "../../wxt.mjs";
4
3
  import { findEntrypoints } from "./find-entrypoints.mjs";
5
4
  import { groupEntrypoints } from "./group-entrypoints.mjs";
5
+ import { formatDuration } from "../time.mjs";
6
6
  import { printBuildSummary } from "../log/printBuildSummary.mjs";
7
7
  import "../log/index.mjs";
8
8
  import { rebuild } from "./rebuild.mjs";
9
9
  import { ValidationError, validateEntrypoints } from "../validation.mjs";
10
- import fs from "fs-extra";
11
- import glob from "fast-glob";
10
+ import { mkdir, rm } from "node:fs/promises";
11
+ import { glob } from "tinyglobby";
12
12
  import { relative } from "node:path";
13
13
  import { mergeJsonOutputs } from "@aklinker1/rollup-plugin-visualizer";
14
- import pc from "picocolors";
14
+ import { styleText } from "node:util";
15
15
  import { isCI } from "ci-info";
16
-
17
16
  //#region src/core/utils/building/internal-build.ts
18
17
  /**
19
- * Builds the extension based on an internal config. No more config discovery is performed, the
20
- * build is based on exactly what is passed in.
18
+ * Builds the extension based on an internal config. No more config discovery is
19
+ * performed, the build is based on exactly what is passed in.
21
20
  *
22
21
  * This function:
22
+ *
23
23
  * 1. Cleans the output directory
24
- * 2. Executes the rebuild function with a blank previous output so everything is built (see
25
- * `rebuild` for more details)
24
+ * 2. Executes the rebuild function with a blank previous output so everything is
25
+ * built (see `rebuild` for more details)
26
26
  * 3. Prints the summary
27
27
  */
28
28
  async function internalBuild() {
29
29
  await wxt.hooks.callHook("build:before", wxt);
30
30
  const verb = wxt.config.command === "serve" ? "Pre-rendering" : "Building";
31
31
  const target = `${wxt.config.browser}-mv${wxt.config.manifestVersion}`;
32
- wxt.logger.info(`${verb} ${pc.cyan(target)} for ${pc.cyan(wxt.config.mode)} with ${pc.green(`${wxt.builder.name} ${wxt.builder.version}`)}`);
32
+ wxt.logger.info(`${verb} ${styleText("cyan", target)} for ${styleText("cyan", wxt.config.mode)} with ${styleText("green", `${wxt.builder.name} ${wxt.builder.version}`)}`);
33
33
  const startTime = Date.now();
34
- await fs.rm(wxt.config.outDir, {
34
+ await rm(wxt.config.outDir, {
35
35
  recursive: true,
36
36
  force: true
37
37
  });
38
- await fs.ensureDir(wxt.config.outDir);
38
+ await mkdir(wxt.config.outDir, { recursive: true });
39
39
  const entrypoints = await findEntrypoints();
40
40
  wxt.logger.debug("Detected entrypoints:", entrypoints);
41
41
  const validationResults = validateEntrypoints(entrypoints);
@@ -50,10 +50,10 @@ async function internalBuild() {
50
50
  if (wxt.config.analysis.enabled) {
51
51
  await combineAnalysisStats();
52
52
  const statsPath = relative(wxt.config.root, wxt.config.analysis.outputFile);
53
- wxt.logger.info(`Analysis complete:\n ${pc.gray("└─")} ${pc.yellow(statsPath)}`);
54
- if (wxt.config.analysis.open) if (isCI) wxt.logger.debug(`Skipped opening ${pc.yellow(statsPath)} in CI`);
53
+ wxt.logger.info(`Analysis complete:\n ${styleText("gray", "└─")} ${styleText("yellow", statsPath)}`);
54
+ if (wxt.config.analysis.open) if (isCI) wxt.logger.debug(`Skipped opening ${styleText("yellow", statsPath)} in CI`);
55
55
  else {
56
- wxt.logger.info(`Opening ${pc.yellow(statsPath)} in browser...`);
56
+ wxt.logger.info(`Opening ${styleText("yellow", statsPath)} in browser...`);
57
57
  const { default: open } = await import("open");
58
58
  await open(wxt.config.analysis.outputFile);
59
59
  }
@@ -63,14 +63,15 @@ async function internalBuild() {
63
63
  async function combineAnalysisStats() {
64
64
  const absolutePaths = (await glob(`${wxt.config.analysis.outputName}-*.json`, {
65
65
  cwd: wxt.config.analysis.outputDir,
66
- absolute: true
66
+ absolute: true,
67
+ expandDirectories: false
67
68
  })).map(unnormalizePath);
68
69
  await mergeJsonOutputs({
69
70
  inputs: absolutePaths,
70
71
  template: wxt.config.analysis.template,
71
72
  filename: wxt.config.analysis.outputFile
72
73
  });
73
- if (!wxt.config.analysis.keepArtifacts) await Promise.all(absolutePaths.map((statsFile) => fs.remove(statsFile)));
74
+ if (!wxt.config.analysis.keepArtifacts) await Promise.all(absolutePaths.map((statsFile) => rm(statsFile, { force: true })));
74
75
  }
75
76
  function printValidationResults({ errorCount, errors, warningCount }) {
76
77
  (errorCount > 0 ? wxt.logger.error : wxt.logger.warn)(`Entrypoint validation failed: ${errorCount} error${errorCount === 1 ? "" : "s"}, ${warningCount} warning${warningCount === 1 ? "" : "s"}`);
@@ -84,13 +85,12 @@ function printValidationResults({ errorCount, errors, warningCount }) {
84
85
  Array.from(entrypointErrors.entries()).forEach(([entrypoint, errors]) => {
85
86
  wxt.logger.log(relative(cwd, entrypoint.inputPath) + "\n");
86
87
  errors.forEach((err) => {
87
- const type = err.type === "error" ? pc.red("ERROR") : pc.yellow("WARN");
88
- const received = pc.dim(`(received: ${JSON.stringify(err.value)})`);
88
+ const type = err.type === "error" ? styleText("red", "ERROR") : styleText("yellow", "WARN");
89
+ const received = styleText("dim", `(received: ${JSON.stringify(err.value)})`);
89
90
  wxt.logger.log(` - ${type} ${err.message} ${received}`);
90
91
  });
91
92
  console.log();
92
93
  });
93
94
  }
94
-
95
95
  //#endregion
96
- export { internalBuild };
96
+ export { internalBuild };
@@ -2,30 +2,31 @@ import { wxt } from "../../wxt.mjs";
2
2
  import { buildEntrypoints } from "./build-entrypoints.mjs";
3
3
  import { generateWxtDir } from "../../generate-wxt-dir.mjs";
4
4
  import { generateManifest, writeManifest } from "../manifest.mjs";
5
-
5
+ import { createSpinner } from "nanospinner";
6
6
  //#region src/core/utils/building/rebuild.ts
7
7
  /**
8
- * Given a configuration, list of entrypoints, and an existing, partial output, build the
9
- * entrypoints and merge the new output with the existing output.
8
+ * Given a configuration, list of entrypoints, and an existing, partial output,
9
+ * build the entrypoints and merge the new output with the existing output.
10
10
  *
11
11
  * This function will:
12
+ *
12
13
  * 1. Generate the .wxt directory's types
13
14
  * 2. Build the `entrypointGroups` (and copies public files)
14
15
  * 3. Generate the latest manifest for all entrypoints
15
16
  * 4. Write the new manifest to the file system
16
17
  *
17
18
  * @param config Internal config containing all the project information.
18
- * @param allEntrypoints List of entrypoints used to generate the types inside .wxt directory.
19
+ * @param allEntrypoints List of entrypoints used to generate the types inside
20
+ * .wxt directory.
19
21
  * @param entrypointGroups The list of entrypoint groups to build.
20
- * @param existingOutput The previous output to combine the rebuild results into. An emptry array if
21
- * this is the first build.
22
+ * @param existingOutput The previous output to combine the rebuild results
23
+ * into. An empty array if this is the first build.
22
24
  */
23
25
  async function rebuild(allEntrypoints, entrypointGroups, existingOutput = {
24
26
  steps: [],
25
27
  publicAssets: []
26
28
  }) {
27
- const { default: ora } = await import("ora");
28
- const spinner = ora(`Preparing...`).start();
29
+ const spinner = createSpinner("Preparing...").start();
29
30
  await generateWxtDir(allEntrypoints).catch((err) => {
30
31
  wxt.logger.warn("Failed to update .wxt directory:", err);
31
32
  if (wxt.config.command === "build") throw err;
@@ -48,6 +49,5 @@ async function rebuild(allEntrypoints, entrypointGroups, existingOutput = {
48
49
  warnings: manifestWarnings
49
50
  };
50
51
  }
51
-
52
52
  //#endregion
53
- export { rebuild };
53
+ export { rebuild };
@@ -1,11 +1,10 @@
1
1
  import { writeFileIfDifferent } from "./fs.mjs";
2
- import fs, { ensureDir } from "fs-extra";
2
+ import { mkdir, readFile } from "node:fs/promises";
3
3
  import { dirname, resolve } from "path";
4
-
5
4
  //#region src/core/utils/cache.ts
6
5
  /**
7
- * A basic file system cache stored at `<srcDir>/.wxt/cache/<key>`. Just caches a string in a
8
- * file for the given key.
6
+ * A basic file system cache stored at `<srcDir>/.wxt/cache/<key>`. Just caches
7
+ * a string in a file for the given key.
9
8
  *
10
9
  * @param srcDir Absolute path to source directory. See `InternalConfig.srcDir`
11
10
  */
@@ -14,19 +13,18 @@ function createFsCache(wxtDir) {
14
13
  return {
15
14
  async set(key, value) {
16
15
  const path = getPath(key);
17
- await ensureDir(dirname(path));
16
+ await mkdir(dirname(path), { recursive: true });
18
17
  await writeFileIfDifferent(path, value);
19
18
  },
20
19
  async get(key) {
21
20
  const path = getPath(key);
22
21
  try {
23
- return await fs.readFile(path, "utf-8");
22
+ return await readFile(path, "utf-8");
24
23
  } catch {
25
24
  return;
26
25
  }
27
26
  }
28
27
  };
29
28
  }
30
-
31
29
  //#endregion
32
- export { createFsCache };
30
+ export { createFsCache };
@@ -1,9 +1,8 @@
1
1
  //#region src/core/utils/constants.ts
2
2
  /**
3
- * Module ID used to build the background in dev mode if the extension doesn't include a background
4
- * script/service worker.
3
+ * Module ID used to build the background in dev mode if the extension doesn't
4
+ * include a background script/service worker.
5
5
  */
6
6
  const VIRTUAL_NOOP_BACKGROUND_MODULE_ID = "virtual:user-background";
7
-
8
7
  //#endregion
9
- export { VIRTUAL_NOOP_BACKGROUND_MODULE_ID };
8
+ export { VIRTUAL_NOOP_BACKGROUND_MODULE_ID };
@@ -1,8 +1,8 @@
1
1
  import { getEntrypointBundlePath } from "./entrypoints.mjs";
2
-
3
2
  //#region src/core/utils/content-scripts.ts
4
3
  /**
5
- * Returns a unique and consistent string hash based on a content scripts options.
4
+ * Returns a unique and consistent string hash based on a content scripts
5
+ * options.
6
6
  *
7
7
  * It is able to recognize default values,
8
8
  */
@@ -56,6 +56,5 @@ function mapWxtOptionsToRegisteredContentScript(options, js, css) {
56
56
  function getContentScriptJs(config, entrypoint) {
57
57
  return [getEntrypointBundlePath(entrypoint, config.outDir, ".js")];
58
58
  }
59
-
60
59
  //#endregion
61
- export { getContentScriptJs, hashContentScriptOptions, mapWxtOptionsToContentScript, mapWxtOptionsToRegisteredContentScript };
60
+ export { getContentScriptJs, hashContentScriptOptions, mapWxtOptionsToContentScript, mapWxtOptionsToRegisteredContentScript };
@@ -14,9 +14,7 @@ var ContentSecurityPolicy = class ContentSecurityPolicy {
14
14
  }, {});
15
15
  else this.data = {};
16
16
  }
17
- /**
18
- * Ensure a set of values are listed under a directive.
19
- */
17
+ /** Ensure a set of values are listed under a directive. */
20
18
  add(directive, ...newValues) {
21
19
  const values = this.data[directive] ?? [];
22
20
  newValues.forEach((newValue) => {
@@ -31,6 +29,5 @@ var ContentSecurityPolicy = class ContentSecurityPolicy {
31
29
  }).map((entry) => entry.flat().join(" ")).join("; ") + ";";
32
30
  }
33
31
  };
34
-
35
32
  //#endregion
36
- export { ContentSecurityPolicy };
33
+ export { ContentSecurityPolicy };
@@ -0,0 +1,158 @@
1
+ import { normalizePath } from "./paths.mjs";
2
+ import { getEntrypointBundlePath, isHtmlEntrypoint } from "./entrypoints.mjs";
3
+ import { filterTruthy, toArray } from "./arrays.mjs";
4
+ import { wxt } from "../wxt.mjs";
5
+ import { detectDevChanges, getRelevantDevChangedFiles } from "./building/detect-dev-changes.mjs";
6
+ import { findEntrypoints } from "./building/find-entrypoints.mjs";
7
+ import { groupEntrypoints } from "./building/group-entrypoints.mjs";
8
+ import { getContentScriptJs, mapWxtOptionsToRegisteredContentScript } from "./content-scripts.mjs";
9
+ import { getContentScriptCssFiles, getContentScriptsCssMap } from "./manifest.mjs";
10
+ import { rebuild } from "./building/rebuild.mjs";
11
+ import "./building/index.mjs";
12
+ import { isBabelSyntaxError, logBabelSyntaxError } from "./syntax-errors.mjs";
13
+ import { relative } from "node:path";
14
+ import { styleText } from "node:util";
15
+ import { Mutex } from "async-mutex";
16
+ //#region src/core/utils/create-file-reloader.ts
17
+ /**
18
+ * Returns a function responsible for reloading different parts of the extension
19
+ * when a file changes.
20
+ */
21
+ function createFileReloader(server) {
22
+ const fileChangedMutex = new Mutex();
23
+ const changeQueue = [];
24
+ let processLoop;
25
+ const processQueue = async () => {
26
+ await fileChangedMutex.runExclusive(async () => {
27
+ const fileChanges = changeQueue.splice(0, changeQueue.length).map(([_, file]) => file);
28
+ if (fileChanges.length === 0) return;
29
+ if (server.currentOutput == null) return;
30
+ const normalizedFileChanges = fileChanges.map(normalizePath);
31
+ const relevantFileChanges = getRelevantDevChangedFiles(fileChanges, server.currentOutput);
32
+ const normalizedEntrypointsDir = normalizePath(wxt.config.entrypointsDir);
33
+ const hasEntrypointDirChange = normalizedFileChanges.some((file) => file === normalizedEntrypointsDir || file.startsWith(`${normalizedEntrypointsDir}/`));
34
+ if (relevantFileChanges.length === 0 && !hasEntrypointDirChange) return;
35
+ await wxt.reloadConfig();
36
+ const allEntrypoints = await findEntrypoints();
37
+ const allEntrypointGroups = groupEntrypoints(allEntrypoints);
38
+ const newEntrypointGroups = getNewEntrypointGroups(normalizedFileChanges, allEntrypointGroups, server.currentOutput);
39
+ if (relevantFileChanges.length === 0 && newEntrypointGroups.length === 0) return;
40
+ const changes = detectDevChanges(relevantFileChanges, server.currentOutput);
41
+ if (changes.type === "full-restart") {
42
+ wxt.logger.info("Config changed, restarting server...");
43
+ server.restart();
44
+ return;
45
+ }
46
+ if (changes.type === "browser-restart") {
47
+ wxt.logger.info("Runner config changed, restarting browser...");
48
+ server.restartBrowser();
49
+ return;
50
+ }
51
+ const hasNewEntrypoints = newEntrypointGroups.length > 0;
52
+ if (changes.type === "no-change" && !hasNewEntrypoints) return;
53
+ const changedFilesToLog = Array.from(new Set([...relevantFileChanges, ...newEntrypointGroups.flatMap((group) => toArray(group)).map((entry) => entry.inputPath)]));
54
+ wxt.logger.info(`Changed: ${changedFilesToLog.map((file) => styleText("dim", relative(wxt.config.root, file))).join(", ")}`);
55
+ const rebuildGroups = changes.type === "no-change" ? newEntrypointGroups : mergeEntrypointGroups(getLatestRebuildGroups(changes.rebuildGroups, allEntrypointGroups), newEntrypointGroups);
56
+ try {
57
+ const { output: newOutput } = await rebuild(allEntrypoints, rebuildGroups, changes.type === "no-change" ? server.currentOutput : changes.cachedOutput);
58
+ server.currentOutput = newOutput;
59
+ if (hasNewEntrypoints || changes.type === "extension-reload") {
60
+ server.reloadExtension();
61
+ wxt.logger.success(`Reloaded extension`);
62
+ } else if (changes.type === "html-reload") {
63
+ const { reloadedNames } = reloadHtmlPages(rebuildGroups, server);
64
+ wxt.logger.success(`Reloaded: ${getFilenameList(reloadedNames)}`);
65
+ } else if (changes.type === "content-script-reload") {
66
+ reloadContentScripts(changes.changedSteps, server);
67
+ const rebuiltNames = rebuildGroups.flat().map((entry) => entry.name);
68
+ wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
69
+ }
70
+ } catch {}
71
+ }).catch((error) => {
72
+ if (!isBabelSyntaxError(error)) throw error;
73
+ logBabelSyntaxError(error);
74
+ });
75
+ };
76
+ const waitForDebounceWindow = async () => {
77
+ await new Promise((resolve) => {
78
+ setTimeout(resolve, wxt.config.dev.server.watchDebounce);
79
+ });
80
+ };
81
+ const queueWorker = async () => {
82
+ while (true) {
83
+ await processQueue();
84
+ await waitForDebounceWindow();
85
+ if (changeQueue.length === 0) break;
86
+ }
87
+ };
88
+ return async (event, path) => {
89
+ changeQueue.push([event, path]);
90
+ processLoop ??= queueWorker().finally(() => {
91
+ processLoop = void 0;
92
+ });
93
+ await processLoop;
94
+ };
95
+ }
96
+ function getNewEntrypointGroups(normalizedFileChanges, allEntrypointGroups, currentOutput) {
97
+ const changedFiles = new Set(normalizedFileChanges);
98
+ const builtEntrypointPaths = new Set(currentOutput.steps.flatMap((step) => toArray(step.entrypoints).map((entry) => normalizePath(entry.inputPath))));
99
+ return allEntrypointGroups.filter((group) => {
100
+ const groupEntrypoints = toArray(group);
101
+ const hasNewEntrypoint = groupEntrypoints.some((entry) => !builtEntrypointPaths.has(normalizePath(entry.inputPath)));
102
+ const changedEntrypoint = groupEntrypoints.some((entry) => changedFiles.has(normalizePath(entry.inputPath)));
103
+ return hasNewEntrypoint && changedEntrypoint;
104
+ });
105
+ }
106
+ function getLatestRebuildGroups(rebuildGroups, allEntrypointGroups) {
107
+ const groupByEntrypointPath = /* @__PURE__ */ new Map();
108
+ allEntrypointGroups.forEach((group) => {
109
+ toArray(group).forEach((entry) => {
110
+ groupByEntrypointPath.set(normalizePath(entry.inputPath), group);
111
+ });
112
+ });
113
+ return mergeEntrypointGroups(rebuildGroups.flatMap((group) => {
114
+ return filterTruthy(toArray(group).map((entry) => groupByEntrypointPath.get(normalizePath(entry.inputPath))));
115
+ }));
116
+ }
117
+ function mergeEntrypointGroups(...groups) {
118
+ const deduped = /* @__PURE__ */ new Map();
119
+ groups.flat().forEach((group) => {
120
+ deduped.set(getEntrypointGroupKey(group), group);
121
+ });
122
+ return [...deduped.values()];
123
+ }
124
+ function getEntrypointGroupKey(group) {
125
+ return toArray(group).map((entry) => normalizePath(entry.inputPath)).sort().join("|");
126
+ }
127
+ /**
128
+ * From the server, tell the client to reload content scripts from the provided
129
+ * build step outputs.
130
+ */
131
+ function reloadContentScripts(steps, server) {
132
+ if (wxt.config.manifestVersion === 3) steps.forEach((step) => {
133
+ if (server.currentOutput == null) return;
134
+ const entry = step.entrypoints;
135
+ if (Array.isArray(entry) || entry.type !== "content-script") return;
136
+ const js = getContentScriptJs(wxt.config, entry);
137
+ const cssMap = getContentScriptsCssMap(server.currentOutput, [entry]);
138
+ const css = getContentScriptCssFiles([entry], cssMap);
139
+ server.reloadContentScript({
140
+ registration: entry.options.registration,
141
+ contentScript: mapWxtOptionsToRegisteredContentScript(entry.options, js, css)
142
+ });
143
+ });
144
+ else server.reloadExtension();
145
+ }
146
+ function reloadHtmlPages(groups, server) {
147
+ const htmlEntries = groups.flat().filter(isHtmlEntrypoint);
148
+ htmlEntries.forEach((entry) => {
149
+ const path = getEntrypointBundlePath(entry, wxt.config.outDir, ".html");
150
+ server.reloadPage(path);
151
+ });
152
+ return { reloadedNames: htmlEntries.map((entry) => entry.name) };
153
+ }
154
+ function getFilenameList(names) {
155
+ return names.map((name) => styleText("cyan", name)).join(styleText("dim", ", "));
156
+ }
157
+ //#endregion
158
+ export { createFileReloader, reloadContentScripts };
@@ -0,0 +1,10 @@
1
+ import { Entrypoint } from "../../types.mjs";
2
+
3
+ //#region src/core/utils/entrypoints.d.ts
4
+ /**
5
+ * Return's the entrypoint's output path relative to the output directory. Used
6
+ * for paths in the manifest and rollup's bundle.
7
+ */
8
+ declare function getEntrypointBundlePath(entrypoint: Entrypoint, outDir: string, ext: string): string;
9
+ //#endregion
10
+ export { getEntrypointBundlePath };
@@ -1,6 +1,5 @@
1
1
  import { normalizePath } from "./paths.mjs";
2
2
  import path, { extname, relative, resolve } from "node:path";
3
-
4
3
  //#region src/core/utils/entrypoints.ts
5
4
  function getEntrypointName(entrypointsDir, inputPath) {
6
5
  return path.relative(entrypointsDir, inputPath).split(/[./\\]/, 2)[0];
@@ -9,15 +8,13 @@ function getEntrypointOutputFile(entrypoint, ext) {
9
8
  return resolve(entrypoint.outputDir, `${entrypoint.name}${ext}`);
10
9
  }
11
10
  /**
12
- * Return's the entrypoint's output path relative to the output directory. Used for paths in the
13
- * manifest and rollup's bundle.
11
+ * Return's the entrypoint's output path relative to the output directory. Used
12
+ * for paths in the manifest and rollup's bundle.
14
13
  */
15
14
  function getEntrypointBundlePath(entrypoint, outDir, ext) {
16
15
  return normalizePath(relative(outDir, getEntrypointOutputFile(entrypoint, ext)));
17
16
  }
18
- /**
19
- * Given an entrypoint option, resolve it's value based on a target browser.
20
- */
17
+ /** Given an entrypoint option, resolve it's value based on a target browser. */
21
18
  function resolvePerBrowserOption(option, browser) {
22
19
  if (typeof option === "object" && !Array.isArray(option)) return option[browser];
23
20
  return option;
@@ -25,8 +22,8 @@ function resolvePerBrowserOption(option, browser) {
25
22
  /**
26
23
  * Given an entrypoint option, resolve it's value based on a target browser.
27
24
  *
28
- * defaultIcon is special, it's the only key that's a record, which can confuse this function. So
29
- * it's been manually excluded from resolution.
25
+ * DefaultIcon is special, it's the only key that's a record, which can confuse
26
+ * this function. So it's been manually excluded from resolution.
30
27
  */
31
28
  function resolvePerBrowserOptions(options, browser) {
32
29
  return Object.fromEntries(Object.entries(options).map(([key, value]) => [key, key === "defaultIcon" ? value : resolvePerBrowserOption(value, browser)]));
@@ -54,6 +51,5 @@ function isJsEntrypoint(entrypoint) {
54
51
  ".tsx"
55
52
  ].includes(ext);
56
53
  }
57
-
58
54
  //#endregion
59
- export { getEntrypointBundlePath, getEntrypointName, isHtmlEntrypoint, isJsEntrypoint, resolvePerBrowserOptions };
55
+ export { getEntrypointBundlePath, getEntrypointName, isHtmlEntrypoint, isJsEntrypoint, resolvePerBrowserOptions };
@@ -1,25 +1,30 @@
1
- import { config } from "dotenv";
2
1
  import { expand } from "dotenv-expand";
3
-
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { parseEnv } from "node:util";
4
4
  //#region src/core/utils/env.ts
5
- /**
6
- * Load environment files based on the current mode and browser.
7
- */
5
+ /** Load environment files based on the current mode and browser. */
8
6
  function loadEnv(mode, browser) {
9
- return expand(config({
10
- quiet: true,
11
- path: [
12
- `.env.${mode}.${browser}.local`,
13
- `.env.${mode}.${browser}`,
14
- `.env.${browser}.local`,
15
- `.env.${browser}`,
16
- `.env.${mode}.local`,
17
- `.env.${mode}`,
18
- `.env.local`,
19
- `.env`
20
- ]
7
+ const envFiles = [
8
+ `.env`,
9
+ `.env.local`,
10
+ `.env.${mode}`,
11
+ `.env.${mode}.local`,
12
+ `.env.${browser}`,
13
+ `.env.${browser}.local`,
14
+ `.env.${mode}.${browser}`,
15
+ `.env.${mode}.${browser}.local`
16
+ ];
17
+ const parsed = Object.fromEntries(envFiles.flatMap((filePath) => {
18
+ if (!existsSync(filePath)) return [];
19
+ try {
20
+ const parsedEnv = parseEnv(readFileSync(filePath, "utf-8"));
21
+ return Object.entries(parsedEnv);
22
+ } catch {
23
+ return [];
24
+ }
21
25
  }));
26
+ expand({ parsed });
27
+ return parsed;
22
28
  }
23
-
24
29
  //#endregion
25
- export { loadEnv };
30
+ export { loadEnv };