@multiplatform.one/vite-plugin-webext 6.6.0 → 7.0.0

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/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # @multiplatform.one/vite-plugin-webext
2
+
3
+ Vite config factories for building a browser web extension (MV3 views,
4
+ background service worker, content scripts) as a target of a
5
+ multiplatform.one One app. Three vite builds cover the three JS worlds
6
+ (DOM extension pages, worker, injected content script);
7
+ `runWebextPrepare` writes the manifest and the dev-mode HMR scaffolding
8
+ into a loadable staging dir:
9
+
10
+ ```
11
+ <stagingDir>/manifest.json
12
+ <stagingDir>/dist/views/<view>/index.html (popup/options/sidepanel/…)
13
+ <stagingDir>/dist/background/index.mjs (service worker / bg script)
14
+ <stagingDir>/dist/contentScripts/index.global.js
15
+ <stagingDir>/dist/contentScripts/webext.css (web-accessible resource)
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ One config file per JS world, plus a prepare script. Every directory
21
+ under `<targetDir>/views/` with an `index.html` becomes an extension
22
+ page.
23
+
24
+ ```ts
25
+ // vite.config.webext.ts — extension pages (popup/options/…)
26
+ import { resolve } from "node:path";
27
+ import { createWebextViewsConfig } from "@multiplatform.one/vite-plugin-webext";
28
+ import { defineConfig } from "vite";
29
+ import packageJson from "./package.json";
30
+
31
+ export default defineConfig(async () =>
32
+ createWebextViewsConfig({
33
+ targetDir: resolve(import.meta.dirname, "webext"),
34
+ stagingDir: resolve(import.meta.dirname, "dist-webext"),
35
+ tamaguiConfig: resolve(import.meta.dirname, "config/tamagui.config.ts"),
36
+ packageInfo: packageJson,
37
+ // MANDATORY for extension builds — see "one must be aliased" below.
38
+ aliases: [{ find: /^one$/, replacement: "@multiplatform.one/router/seam" }],
39
+ }),
40
+ );
41
+ ```
42
+
43
+ `createWebextBackgroundConfig` (sync) and `createWebextContentConfig`
44
+ (async) take the same options for the worker and content-script builds;
45
+ `runWebextPrepare({ targetDir, stagingDir, getManifest, isDev, port,
46
+ watch })` stages the manifest, static assets, and dev HMR scaffolding.
47
+
48
+ ## Required dependencies (the real consumer contract)
49
+
50
+ Declared peers — install them next to this plugin:
51
+
52
+ | package | why |
53
+ | ---------------------- | --------------------------------------------------------------------- |
54
+ | `vite` >=5 | the build tool itself |
55
+ | `@vitejs/plugin-react` | views build (react fast-refresh + jsx). Lazy-imported at factory call |
56
+ | `@tamagui/vite-plugin` | views + content builds (static extraction). Lazy-imported |
57
+
58
+ Beyond the declared peers, a real app build needs:
59
+
60
+ - **`@tamagui/web` as a direct dependency of your app.** The Tamagui
61
+ static extractor bundles your `tamaguiConfig` to
62
+ `<cwd>/.tamagui/tamagui.config.{cjs,mjs}` and requires it from there,
63
+ with `@tamagui/web` left external. Under pnpm's default isolated
64
+ `node_modules`, transitive packages are not resolvable from your app,
65
+ so the extractor fails with "Error bundling tamagui config: Cannot
66
+ find package '@tamagui/web'…" — and (upstream) swallows it, silently
67
+ shipping a deoptimized runtime-styled build. This plugin turns that
68
+ into a hard error in production builds; fix it by adding
69
+ `@tamagui/web` to your app's `dependencies`.
70
+ - **`react-i18next` + `i18next`, if you render the i18n-using catalog
71
+ components.** `@multiplatform.one/components` translates built-in
72
+ copy (Alert, Toast, Carousel, ConfirmDialog, Pagination, views/,
73
+ layouts/, …) via an optional `react-i18next` peer. Apps that never
74
+ render those components may omit both packages and tree-shake the
75
+ components away; apps that do render them get a named runtime error
76
+ telling you to install `react-i18next` and `i18next`.
77
+
78
+ ## `one` must be aliased to the router seam
79
+
80
+ Extension pages must NOT bundle the real `one` runtime: it drags in
81
+ `@react-navigation/core` (and server-only modules) and the build fails.
82
+ This is mandatory, not stylistic — alias `one` to the stack-navigation
83
+ seam in every webext config that renders shared feature code:
84
+
85
+ ```ts
86
+ aliases: [{ find: /^one$/, replacement: "@multiplatform.one/router/seam" }],
87
+ ```
88
+
89
+ The seam (`@multiplatform.one/router/seam`) implements One's router API
90
+ over DOM pages, so shared `features/` code written against One's router
91
+ works unchanged inside the extension.
92
+
93
+ Relatedly, `one`'s client code imports `./vite/one-server-only.mjs`
94
+ (node-only); the factories redirect any `one-server-only` id to one's
95
+ published browser/native no-op. The redirect resolves the installed
96
+ `one` package through node resolution (build cwd, then
97
+ `workspaceRoot`); when `one` is not installed at all the redirect is
98
+ skipped with a one-line warning and normal resolution proceeds.
99
+
100
+ ## `workspaceRoot` semantics
101
+
102
+ `workspaceRoot` defaults to the git toplevel (`git rev-parse
103
+ --show-toplevel`) of the build cwd. It is used for two things:
104
+
105
+ 1. **Workspace source aliases.** Inside the multiplatform.one monorepo,
106
+ every `public/*` package (and its subpath exports) is aliased to its
107
+ TypeScript source so builds never consume stale `dist` artifacts.
108
+ Out-of-repo there is no `public/` under the root, the aliasing
109
+ no-ops, and `@multiplatform.one/*` resolves from `node_modules` like
110
+ any other package — the factories log one line when this happens:
111
+ `no public/ under <root> — resolving @multiplatform.one/* from
112
+ node_modules`.
113
+ 2. **The one-server-only redirect** falls back to resolving `one` from
114
+ `workspaceRoot` when it cannot be resolved from the build cwd.
115
+
116
+ Pass `workspaceRoot` explicitly when your app builds from a directory
117
+ whose git toplevel is not the dependency root (e.g. a nested repo).
package/lib/index.cjs CHANGED
@@ -28,13 +28,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  //#endregion
29
29
  let node_fs = require("node:fs");
30
30
  node_fs = __toESM(node_fs, 1);
31
+ let node_module = require("node:module");
31
32
  let node_path = require("node:path");
32
33
  node_path = __toESM(node_path, 1);
33
34
  let _multiplatform_one_utils_dev = require("@multiplatform.one/utils/dev");
34
- let _vitejs_plugin_react = require("@vitejs/plugin-react");
35
- _vitejs_plugin_react = __toESM(_vitejs_plugin_react, 1);
35
+ let _rolldown_plugin_babel = require("@rolldown/plugin-babel");
36
+ _rolldown_plugin_babel = __toESM(_rolldown_plugin_babel, 1);
36
37
 
37
38
  //#region src/index.ts
39
+ const PLUGIN_NAME = "@multiplatform.one/vite-plugin-webext";
40
+ const logger = console;
38
41
  const WEBEXT_EXTENSIONS = [
39
42
  ".webext.ts",
40
43
  ".webext.tsx",
@@ -76,13 +79,91 @@ function shikiStubPlugin() {
76
79
  }
77
80
  };
78
81
  }
82
+ function barePackageName(specifier) {
83
+ if (!specifier || /^[./#\\]/.test(specifier)) return;
84
+ if (/^(node|data|file|https?):/.test(specifier)) return;
85
+ const packageName = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
86
+ if (node_module.builtinModules.includes(packageName)) return;
87
+ return packageName;
88
+ }
89
+ /** Node ESM bare-import resolution walks parent node_modules dirs and — unlike
90
+ * CJS — ignores NODE_PATH. Presence of the package dir is what we probe. */
91
+ function packageVisibleFromDir(packageName, fromDir) {
92
+ let dir = fromDir;
93
+ while (true) {
94
+ if (node_fs.default.existsSync(node_path.default.join(dir, "node_modules", packageName, "package.json"))) return true;
95
+ const parent = node_path.default.dirname(dir);
96
+ if (parent === dir) return false;
97
+ dir = parent;
98
+ }
99
+ }
100
+ /**
101
+ * The Tamagui static extractor bundles the app's tamagui config to
102
+ * `<cwd>/.tamagui/tamagui.config.{cjs,mjs}` and then loads it (ESM `import()`
103
+ * for .mjs, `require()` for .cjs), leaving its externals (`@tamagui/web`,
104
+ * `@tamagui/core`, react…) as live imports resolved FROM that artifact. When
105
+ * one of them cannot be resolved there (pnpm's default isolated layout
106
+ * without a direct `@tamagui/web` dependency), the extractor logs "Error
107
+ * bundling tamagui config: Cannot find package …", swallows the error, and
108
+ * the build exits 0 with a silently deoptimized runtime-styled bundle.
109
+ * Production builds must fail instead: after the build, replay the artifact's
110
+ * imports with the loader semantics the extractor would use — CJS resolution
111
+ * for .cjs (honors NODE_PATH, which pnpm bin shims set), an ESM-style
112
+ * node_modules walk for .mjs (ESM ignores NODE_PATH — exactly the divergence
113
+ * that breaks the .mjs artifact under pnpm) — and name the missing package.
114
+ * A missing artifact (bundling died before writing it) is equally fatal.
115
+ */
116
+ function tamaguiConfigBundleGuard(isDev) {
117
+ return {
118
+ name: "webext-tamagui-config-guard",
119
+ closeBundle() {
120
+ if (isDev) return;
121
+ const dotDir = node_path.default.join(process.cwd(), ".tamagui");
122
+ const artifacts = ["tamagui.config.cjs", "tamagui.config.mjs"].map((name) => node_path.default.join(dotDir, name)).filter((file) => node_fs.default.existsSync(file));
123
+ if (artifacts.length === 0) {
124
+ if (!node_fs.default.existsSync(dotDir)) return;
125
+ throw new Error(`[${PLUGIN_NAME}] the Tamagui static extractor did not produce its bundled config (expected ${node_path.default.join(dotDir, "tamagui.config.{cjs,mjs}")}). The build would silently fall back to a deoptimized runtime-styled bundle. Re-run with DEBUG=tamagui to see the underlying bundling error.`);
126
+ }
127
+ const artifact = artifacts.sort((a, b) => node_fs.default.statSync(b).mtimeMs - node_fs.default.statSync(a).mtimeMs)[0];
128
+ const source = node_fs.default.readFileSync(artifact, "utf8");
129
+ const requireFromArtifact = (0, node_module.createRequire)(artifact);
130
+ const importRe = /(?:require\s*\(\s*|import\s*\(\s*|from\s*|import\s*)["']([^"'\n]+)["']/g;
131
+ const missing = /* @__PURE__ */ new Set();
132
+ for (const match of source.matchAll(importRe)) {
133
+ const packageName = barePackageName(match[1]);
134
+ if (!packageName) continue;
135
+ if (artifact.endsWith(".cjs")) try {
136
+ requireFromArtifact.resolve(packageName);
137
+ } catch {
138
+ missing.add(packageName);
139
+ }
140
+ else if (!packageVisibleFromDir(packageName, dotDir)) missing.add(packageName);
141
+ }
142
+ if (missing.size > 0) {
143
+ const names = [...missing].map((name) => `"${name}"`).join(", ");
144
+ throw new Error(`[${PLUGIN_NAME}] Tamagui config bundling failed: ${names} cannot be resolved from ${artifact}. The Tamagui static extractor swallows this ("Error bundling tamagui config") and ships a deoptimized runtime-styled build. Fix: add ${names} to your app's dependencies — under pnpm's default isolated node_modules, transitive packages are not resolvable from the app.`);
145
+ }
146
+ }
147
+ };
148
+ }
79
149
  function resolveDefaults(options) {
150
+ const workspaceRootInferred = options.workspaceRoot == null;
80
151
  return {
81
152
  workspaceRoot: options.workspaceRoot ?? (0, _multiplatform_one_utils_dev.lookupProjectRoot)(),
153
+ workspaceRootInferred,
82
154
  isDev: options.isDev ?? process.env.NODE_ENV !== "production",
83
155
  port: options.port ?? (Number(process.env.PORT) || 3303)
84
156
  };
85
157
  }
158
+ /** Peers stay lazy so requiring them is deferred to the factory that needs
159
+ * them — but a missing peer must name itself, not crash the config file. */
160
+ async function importPeer(load, specifier) {
161
+ try {
162
+ return await load();
163
+ } catch (err) {
164
+ throw new Error(`[${PLUGIN_NAME}] could not import its peer dependency "${specifier}" — install it next to ${PLUGIN_NAME} (it is a required peer): ${err instanceof Error ? err.message : err}`);
165
+ }
166
+ }
86
167
  function baseDefine(options, isDev) {
87
168
  return {
88
169
  __DEV__: isDev,
@@ -92,10 +173,16 @@ function baseDefine(options, isDev) {
92
173
  ...options.define
93
174
  };
94
175
  }
95
- function baseResolve(options, workspaceRoot) {
176
+ const loggedNoPublicRoots = /* @__PURE__ */ new Set();
177
+ function baseResolve(options, workspaceRoot, workspaceRootInferred) {
178
+ const sourceAliases = (0, _multiplatform_one_utils_dev.workspaceSourceAliases)(workspaceRoot);
179
+ if (workspaceRootInferred && Object.keys(sourceAliases).length === 0 && !loggedNoPublicRoots.has(workspaceRoot)) {
180
+ loggedNoPublicRoots.add(workspaceRoot);
181
+ logger.info(`[${PLUGIN_NAME}] no public/ under ${workspaceRoot} — resolving @multiplatform.one/* from node_modules`);
182
+ }
96
183
  return {
97
184
  alias: [
98
- ...Object.entries((0, _multiplatform_one_utils_dev.workspaceSourceAliases)(workspaceRoot)).map(([find, replacement]) => ({
185
+ ...Object.entries(sourceAliases).map(([find, replacement]) => ({
99
186
  find,
100
187
  replacement
101
188
  })),
@@ -135,8 +222,9 @@ function listWebextViews(targetDir) {
135
222
  }
136
223
  /** The extension pages build (popup/options/sidepanel/devtools…). */
137
224
  async function createWebextViewsConfig(options) {
138
- const { workspaceRoot, isDev, port } = resolveDefaults(options);
139
- const { tamaguiPlugin } = await import("@tamagui/vite-plugin");
225
+ const { workspaceRoot, workspaceRootInferred, isDev, port } = resolveDefaults(options);
226
+ const { default: react, reactCompilerPreset } = await importPeer(() => import("@vitejs/plugin-react"), "@vitejs/plugin-react");
227
+ const { tamaguiPlugin } = await importPeer(() => import("@tamagui/vite-plugin"), "@tamagui/vite-plugin");
140
228
  const views = listWebextViews(options.targetDir);
141
229
  return {
142
230
  root: options.targetDir,
@@ -144,12 +232,14 @@ async function createWebextViewsConfig(options) {
144
232
  plugins: [
145
233
  (0, _multiplatform_one_utils_dev.stubExpoTypeDeclarations)(),
146
234
  (0, _multiplatform_one_utils_dev.oneServerOnlyBrowserStub)(workspaceRoot),
147
- (0, _vitejs_plugin_react.default)({ jsxRuntime: "automatic" }),
235
+ react({ jsxRuntime: "automatic" }),
236
+ (0, _rolldown_plugin_babel.default)({ presets: [reactCompilerPreset()] }),
148
237
  tamaguiPlugin({
149
238
  components: (0, _multiplatform_one_utils_dev.lookupTamaguiModules)([options.targetDir]),
150
239
  config: options.tamaguiConfig,
151
240
  outputCSS: node_path.default.join(options.targetDir, "tamagui.css")
152
- })
241
+ }),
242
+ tamaguiConfigBundleGuard(isDev)
153
243
  ],
154
244
  define: baseDefine(options, isDev),
155
245
  optimizeDeps: baseOptimizeDeps(),
@@ -157,7 +247,7 @@ async function createWebextViewsConfig(options) {
157
247
  jsx: "automatic",
158
248
  target: "esnext"
159
249
  },
160
- resolve: baseResolve(options, workspaceRoot),
250
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
161
251
  server: {
162
252
  port,
163
253
  hmr: {
@@ -182,7 +272,7 @@ async function createWebextViewsConfig(options) {
182
272
  /** The background service-worker (chromium) / background-script (firefox)
183
273
  * build — a single-file iife, no DOM. */
184
274
  function createWebextBackgroundConfig(options) {
185
- const { workspaceRoot, isDev } = resolveDefaults(options);
275
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
186
276
  return {
187
277
  root: options.targetDir,
188
278
  plugins: [(0, _multiplatform_one_utils_dev.oneServerOnlyBrowserStub)(workspaceRoot)],
@@ -192,7 +282,7 @@ function createWebextBackgroundConfig(options) {
192
282
  jsx: "automatic",
193
283
  target: "esnext"
194
284
  },
195
- resolve: baseResolve(options, workspaceRoot),
285
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
196
286
  build: {
197
287
  watch: isDev ? {} : void 0,
198
288
  outDir: node_path.default.join(options.stagingDir, "dist/background"),
@@ -214,19 +304,23 @@ function createWebextBackgroundConfig(options) {
214
304
  /** The content-script build — a single-file iife injected into pages, with
215
305
  * Tamagui CSS extracted separately (webext.css web-accessible resource). */
216
306
  async function createWebextContentConfig(options) {
217
- const { workspaceRoot, isDev } = resolveDefaults(options);
218
- const { tamaguiPlugin } = await import("@tamagui/vite-plugin");
307
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
308
+ const { default: react, reactCompilerPreset } = await importPeer(() => import("@vitejs/plugin-react"), "@vitejs/plugin-react");
309
+ const { tamaguiPlugin } = await importPeer(() => import("@tamagui/vite-plugin"), "@tamagui/vite-plugin");
219
310
  return {
220
311
  root: options.targetDir,
221
312
  plugins: [
222
313
  (0, _multiplatform_one_utils_dev.stubExpoTypeDeclarations)(),
223
314
  (0, _multiplatform_one_utils_dev.oneServerOnlyBrowserStub)(workspaceRoot),
224
315
  shikiStubPlugin(),
316
+ react({ jsxRuntime: "automatic" }),
317
+ (0, _rolldown_plugin_babel.default)({ presets: [reactCompilerPreset()] }),
225
318
  tamaguiPlugin({
226
319
  components: (0, _multiplatform_one_utils_dev.lookupTamaguiModules)([options.targetDir]),
227
320
  config: options.tamaguiConfig,
228
321
  outputCSS: node_path.default.join(options.targetDir, "tamagui.content.css")
229
- })
322
+ }),
323
+ tamaguiConfigBundleGuard(isDev)
230
324
  ],
231
325
  define: {
232
326
  ...baseDefine(options, isDev),
@@ -237,7 +331,7 @@ async function createWebextContentConfig(options) {
237
331
  jsx: "automatic",
238
332
  target: "esnext"
239
333
  },
240
- resolve: baseResolve(options, workspaceRoot),
334
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
241
335
  build: {
242
336
  watch: isDev ? {} : void 0,
243
337
  outDir: node_path.default.join(options.stagingDir, "dist/contentScripts"),
package/lib/index.mjs CHANGED
@@ -1,9 +1,12 @@
1
+ import { builtinModules, createRequire } from "node:module";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { lookupProjectRoot, lookupTamaguiModules, oneServerOnlyBrowserStub, stubExpoTypeDeclarations, workspaceSourceAliases } from "@multiplatform.one/utils/dev";
4
- import react from "@vitejs/plugin-react";
5
+ import babel from "@rolldown/plugin-babel";
5
6
 
6
7
  //#region src/index.ts
8
+ const PLUGIN_NAME = "@multiplatform.one/vite-plugin-webext";
9
+ const logger = console;
7
10
  const WEBEXT_EXTENSIONS = [
8
11
  ".webext.ts",
9
12
  ".webext.tsx",
@@ -45,13 +48,91 @@ function shikiStubPlugin() {
45
48
  }
46
49
  };
47
50
  }
51
+ function barePackageName(specifier) {
52
+ if (!specifier || /^[./#\\]/.test(specifier)) return;
53
+ if (/^(node|data|file|https?):/.test(specifier)) return;
54
+ const packageName = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
55
+ if (builtinModules.includes(packageName)) return;
56
+ return packageName;
57
+ }
58
+ /** Node ESM bare-import resolution walks parent node_modules dirs and — unlike
59
+ * CJS — ignores NODE_PATH. Presence of the package dir is what we probe. */
60
+ function packageVisibleFromDir(packageName, fromDir) {
61
+ let dir = fromDir;
62
+ while (true) {
63
+ if (fs.existsSync(path.join(dir, "node_modules", packageName, "package.json"))) return true;
64
+ const parent = path.dirname(dir);
65
+ if (parent === dir) return false;
66
+ dir = parent;
67
+ }
68
+ }
69
+ /**
70
+ * The Tamagui static extractor bundles the app's tamagui config to
71
+ * `<cwd>/.tamagui/tamagui.config.{cjs,mjs}` and then loads it (ESM `import()`
72
+ * for .mjs, `require()` for .cjs), leaving its externals (`@tamagui/web`,
73
+ * `@tamagui/core`, react…) as live imports resolved FROM that artifact. When
74
+ * one of them cannot be resolved there (pnpm's default isolated layout
75
+ * without a direct `@tamagui/web` dependency), the extractor logs "Error
76
+ * bundling tamagui config: Cannot find package …", swallows the error, and
77
+ * the build exits 0 with a silently deoptimized runtime-styled bundle.
78
+ * Production builds must fail instead: after the build, replay the artifact's
79
+ * imports with the loader semantics the extractor would use — CJS resolution
80
+ * for .cjs (honors NODE_PATH, which pnpm bin shims set), an ESM-style
81
+ * node_modules walk for .mjs (ESM ignores NODE_PATH — exactly the divergence
82
+ * that breaks the .mjs artifact under pnpm) — and name the missing package.
83
+ * A missing artifact (bundling died before writing it) is equally fatal.
84
+ */
85
+ function tamaguiConfigBundleGuard(isDev) {
86
+ return {
87
+ name: "webext-tamagui-config-guard",
88
+ closeBundle() {
89
+ if (isDev) return;
90
+ const dotDir = path.join(process.cwd(), ".tamagui");
91
+ const artifacts = ["tamagui.config.cjs", "tamagui.config.mjs"].map((name) => path.join(dotDir, name)).filter((file) => fs.existsSync(file));
92
+ if (artifacts.length === 0) {
93
+ if (!fs.existsSync(dotDir)) return;
94
+ throw new Error(`[${PLUGIN_NAME}] the Tamagui static extractor did not produce its bundled config (expected ${path.join(dotDir, "tamagui.config.{cjs,mjs}")}). The build would silently fall back to a deoptimized runtime-styled bundle. Re-run with DEBUG=tamagui to see the underlying bundling error.`);
95
+ }
96
+ const artifact = artifacts.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0];
97
+ const source = fs.readFileSync(artifact, "utf8");
98
+ const requireFromArtifact = createRequire(artifact);
99
+ const importRe = /(?:require\s*\(\s*|import\s*\(\s*|from\s*|import\s*)["']([^"'\n]+)["']/g;
100
+ const missing = /* @__PURE__ */ new Set();
101
+ for (const match of source.matchAll(importRe)) {
102
+ const packageName = barePackageName(match[1]);
103
+ if (!packageName) continue;
104
+ if (artifact.endsWith(".cjs")) try {
105
+ requireFromArtifact.resolve(packageName);
106
+ } catch {
107
+ missing.add(packageName);
108
+ }
109
+ else if (!packageVisibleFromDir(packageName, dotDir)) missing.add(packageName);
110
+ }
111
+ if (missing.size > 0) {
112
+ const names = [...missing].map((name) => `"${name}"`).join(", ");
113
+ throw new Error(`[${PLUGIN_NAME}] Tamagui config bundling failed: ${names} cannot be resolved from ${artifact}. The Tamagui static extractor swallows this ("Error bundling tamagui config") and ships a deoptimized runtime-styled build. Fix: add ${names} to your app's dependencies — under pnpm's default isolated node_modules, transitive packages are not resolvable from the app.`);
114
+ }
115
+ }
116
+ };
117
+ }
48
118
  function resolveDefaults(options) {
119
+ const workspaceRootInferred = options.workspaceRoot == null;
49
120
  return {
50
121
  workspaceRoot: options.workspaceRoot ?? lookupProjectRoot(),
122
+ workspaceRootInferred,
51
123
  isDev: options.isDev ?? process.env.NODE_ENV !== "production",
52
124
  port: options.port ?? (Number(process.env.PORT) || 3303)
53
125
  };
54
126
  }
127
+ /** Peers stay lazy so requiring them is deferred to the factory that needs
128
+ * them — but a missing peer must name itself, not crash the config file. */
129
+ async function importPeer(load, specifier) {
130
+ try {
131
+ return await load();
132
+ } catch (err) {
133
+ throw new Error(`[${PLUGIN_NAME}] could not import its peer dependency "${specifier}" — install it next to ${PLUGIN_NAME} (it is a required peer): ${err instanceof Error ? err.message : err}`);
134
+ }
135
+ }
55
136
  function baseDefine(options, isDev) {
56
137
  return {
57
138
  __DEV__: isDev,
@@ -61,10 +142,16 @@ function baseDefine(options, isDev) {
61
142
  ...options.define
62
143
  };
63
144
  }
64
- function baseResolve(options, workspaceRoot) {
145
+ const loggedNoPublicRoots = /* @__PURE__ */ new Set();
146
+ function baseResolve(options, workspaceRoot, workspaceRootInferred) {
147
+ const sourceAliases = workspaceSourceAliases(workspaceRoot);
148
+ if (workspaceRootInferred && Object.keys(sourceAliases).length === 0 && !loggedNoPublicRoots.has(workspaceRoot)) {
149
+ loggedNoPublicRoots.add(workspaceRoot);
150
+ logger.info(`[${PLUGIN_NAME}] no public/ under ${workspaceRoot} — resolving @multiplatform.one/* from node_modules`);
151
+ }
65
152
  return {
66
153
  alias: [
67
- ...Object.entries(workspaceSourceAliases(workspaceRoot)).map(([find, replacement]) => ({
154
+ ...Object.entries(sourceAliases).map(([find, replacement]) => ({
68
155
  find,
69
156
  replacement
70
157
  })),
@@ -104,8 +191,9 @@ function listWebextViews(targetDir) {
104
191
  }
105
192
  /** The extension pages build (popup/options/sidepanel/devtools…). */
106
193
  async function createWebextViewsConfig(options) {
107
- const { workspaceRoot, isDev, port } = resolveDefaults(options);
108
- const { tamaguiPlugin } = await import("@tamagui/vite-plugin");
194
+ const { workspaceRoot, workspaceRootInferred, isDev, port } = resolveDefaults(options);
195
+ const { default: react, reactCompilerPreset } = await importPeer(() => import("@vitejs/plugin-react"), "@vitejs/plugin-react");
196
+ const { tamaguiPlugin } = await importPeer(() => import("@tamagui/vite-plugin"), "@tamagui/vite-plugin");
109
197
  const views = listWebextViews(options.targetDir);
110
198
  return {
111
199
  root: options.targetDir,
@@ -114,11 +202,13 @@ async function createWebextViewsConfig(options) {
114
202
  stubExpoTypeDeclarations(),
115
203
  oneServerOnlyBrowserStub(workspaceRoot),
116
204
  react({ jsxRuntime: "automatic" }),
205
+ babel({ presets: [reactCompilerPreset()] }),
117
206
  tamaguiPlugin({
118
207
  components: lookupTamaguiModules([options.targetDir]),
119
208
  config: options.tamaguiConfig,
120
209
  outputCSS: path.join(options.targetDir, "tamagui.css")
121
- })
210
+ }),
211
+ tamaguiConfigBundleGuard(isDev)
122
212
  ],
123
213
  define: baseDefine(options, isDev),
124
214
  optimizeDeps: baseOptimizeDeps(),
@@ -126,7 +216,7 @@ async function createWebextViewsConfig(options) {
126
216
  jsx: "automatic",
127
217
  target: "esnext"
128
218
  },
129
- resolve: baseResolve(options, workspaceRoot),
219
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
130
220
  server: {
131
221
  port,
132
222
  hmr: {
@@ -151,7 +241,7 @@ async function createWebextViewsConfig(options) {
151
241
  /** The background service-worker (chromium) / background-script (firefox)
152
242
  * build — a single-file iife, no DOM. */
153
243
  function createWebextBackgroundConfig(options) {
154
- const { workspaceRoot, isDev } = resolveDefaults(options);
244
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
155
245
  return {
156
246
  root: options.targetDir,
157
247
  plugins: [oneServerOnlyBrowserStub(workspaceRoot)],
@@ -161,7 +251,7 @@ function createWebextBackgroundConfig(options) {
161
251
  jsx: "automatic",
162
252
  target: "esnext"
163
253
  },
164
- resolve: baseResolve(options, workspaceRoot),
254
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
165
255
  build: {
166
256
  watch: isDev ? {} : void 0,
167
257
  outDir: path.join(options.stagingDir, "dist/background"),
@@ -183,19 +273,23 @@ function createWebextBackgroundConfig(options) {
183
273
  /** The content-script build — a single-file iife injected into pages, with
184
274
  * Tamagui CSS extracted separately (webext.css web-accessible resource). */
185
275
  async function createWebextContentConfig(options) {
186
- const { workspaceRoot, isDev } = resolveDefaults(options);
187
- const { tamaguiPlugin } = await import("@tamagui/vite-plugin");
276
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
277
+ const { default: react, reactCompilerPreset } = await importPeer(() => import("@vitejs/plugin-react"), "@vitejs/plugin-react");
278
+ const { tamaguiPlugin } = await importPeer(() => import("@tamagui/vite-plugin"), "@tamagui/vite-plugin");
188
279
  return {
189
280
  root: options.targetDir,
190
281
  plugins: [
191
282
  stubExpoTypeDeclarations(),
192
283
  oneServerOnlyBrowserStub(workspaceRoot),
193
284
  shikiStubPlugin(),
285
+ react({ jsxRuntime: "automatic" }),
286
+ babel({ presets: [reactCompilerPreset()] }),
194
287
  tamaguiPlugin({
195
288
  components: lookupTamaguiModules([options.targetDir]),
196
289
  config: options.tamaguiConfig,
197
290
  outputCSS: path.join(options.targetDir, "tamagui.content.css")
198
- })
291
+ }),
292
+ tamaguiConfigBundleGuard(isDev)
199
293
  ],
200
294
  define: {
201
295
  ...baseDefine(options, isDev),
@@ -206,7 +300,7 @@ async function createWebextContentConfig(options) {
206
300
  jsx: "automatic",
207
301
  target: "esnext"
208
302
  },
209
- resolve: baseResolve(options, workspaceRoot),
303
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
210
304
  build: {
211
305
  watch: isDev ? {} : void 0,
212
306
  outDir: path.join(options.stagingDir, "dist/contentScripts"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/vite-plugin-webext",
3
- "version": "6.6.0",
3
+ "version": "7.0.0",
4
4
  "description": "Vite config factories for building a browser web extension (MV3 views, background service worker, content scripts) as a target of a multiplatform.one One app — workspace source aliases, the one-server-only browser stub, Tamagui wiring, and the manifest/dev-reload prepare pipeline.",
5
5
  "keywords": [
6
6
  "browser-extension",
@@ -46,11 +46,15 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
+ "@babel/core": "^7.26.0",
50
+ "@rolldown/plugin-babel": "^0.2.3",
51
+ "babel-plugin-react-compiler": "^1.0.0",
49
52
  "chokidar": "^5.0.0",
50
- "@multiplatform.one/utils": "6.6.0"
53
+ "@multiplatform.one/utils": "7.0.0"
51
54
  },
52
55
  "devDependencies": {
53
- "@tamagui/vite-plugin": "2.0.0-rc.41",
56
+ "@tamagui/vite-plugin": "2.7.6",
57
+ "@types/babel__core": "^7.20.5",
54
58
  "@types/node": "^25.6.0",
55
59
  "@vitejs/plugin-react": "^6.0.1",
56
60
  "typescript": "~5.9.3",
@@ -62,7 +66,7 @@
62
66
  "vite": ">=5"
63
67
  },
64
68
  "scripts": {
65
- "typecheck": "tsc --noEmit",
69
+ "typecheck": "tsgo --noEmit",
66
70
  "build": "rm -rf lib types 2>/dev/null && tsc -b --emitDeclarationOnly && tsdown"
67
71
  }
68
72
  }
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@
17
17
  // @multiplatform.one/utils/dev so every product app shares one copy.
18
18
 
19
19
  import fs from "node:fs";
20
+ import { builtinModules, createRequire } from "node:module";
20
21
  import path from "node:path";
21
22
  import {
22
23
  lookupTamaguiModules,
@@ -25,9 +26,13 @@ import {
25
26
  workspaceSourceAliases,
26
27
  lookupProjectRoot,
27
28
  } from "@multiplatform.one/utils/dev";
28
- import react from "@vitejs/plugin-react";
29
+ import babel from "@rolldown/plugin-babel";
29
30
  import type { Alias, Plugin, UserConfig } from "vite";
30
31
 
32
+ const PLUGIN_NAME = "@multiplatform.one/vite-plugin-webext";
33
+
34
+ const logger = console;
35
+
31
36
  export interface WebextPackageInfo {
32
37
  name: string;
33
38
  displayName?: string;
@@ -103,11 +108,118 @@ export function shikiStubPlugin(): Plugin {
103
108
  };
104
109
  }
105
110
 
111
+ function barePackageName(specifier: string): string | undefined {
112
+ if (!specifier || /^[./#\\]/.test(specifier)) return;
113
+ if (/^(node|data|file|https?):/.test(specifier)) return;
114
+ const packageName = specifier.startsWith("@")
115
+ ? specifier.split("/").slice(0, 2).join("/")
116
+ : specifier.split("/")[0]!;
117
+ if (builtinModules.includes(packageName)) return;
118
+ return packageName;
119
+ }
120
+
121
+ /** Node ESM bare-import resolution walks parent node_modules dirs and — unlike
122
+ * CJS — ignores NODE_PATH. Presence of the package dir is what we probe. */
123
+ function packageVisibleFromDir(packageName: string, fromDir: string): boolean {
124
+ let dir = fromDir;
125
+ while (true) {
126
+ if (fs.existsSync(path.join(dir, "node_modules", packageName, "package.json"))) return true;
127
+ const parent = path.dirname(dir);
128
+ if (parent === dir) return false;
129
+ dir = parent;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * The Tamagui static extractor bundles the app's tamagui config to
135
+ * `<cwd>/.tamagui/tamagui.config.{cjs,mjs}` and then loads it (ESM `import()`
136
+ * for .mjs, `require()` for .cjs), leaving its externals (`@tamagui/web`,
137
+ * `@tamagui/core`, react…) as live imports resolved FROM that artifact. When
138
+ * one of them cannot be resolved there (pnpm's default isolated layout
139
+ * without a direct `@tamagui/web` dependency), the extractor logs "Error
140
+ * bundling tamagui config: Cannot find package …", swallows the error, and
141
+ * the build exits 0 with a silently deoptimized runtime-styled bundle.
142
+ * Production builds must fail instead: after the build, replay the artifact's
143
+ * imports with the loader semantics the extractor would use — CJS resolution
144
+ * for .cjs (honors NODE_PATH, which pnpm bin shims set), an ESM-style
145
+ * node_modules walk for .mjs (ESM ignores NODE_PATH — exactly the divergence
146
+ * that breaks the .mjs artifact under pnpm) — and name the missing package.
147
+ * A missing artifact (bundling died before writing it) is equally fatal.
148
+ */
149
+ function tamaguiConfigBundleGuard(isDev: boolean): Plugin {
150
+ return {
151
+ name: "webext-tamagui-config-guard",
152
+ closeBundle() {
153
+ if (isDev) return;
154
+ const dotDir = path.join(process.cwd(), ".tamagui");
155
+ const artifacts = ["tamagui.config.cjs", "tamagui.config.mjs"]
156
+ .map((name) => path.join(dotDir, name))
157
+ .filter((file) => fs.existsSync(file));
158
+ if (artifacts.length === 0) {
159
+ // No .tamagui dir at all means the extractor never ran for this
160
+ // build (nothing to verify) — an existing dir without the config
161
+ // artifact means bundling died before writing it.
162
+ if (!fs.existsSync(dotDir)) return;
163
+ throw new Error(
164
+ `[${PLUGIN_NAME}] the Tamagui static extractor did not produce its bundled config ` +
165
+ `(expected ${path.join(dotDir, "tamagui.config.{cjs,mjs}")}). The build would ` +
166
+ "silently fall back to a deoptimized runtime-styled bundle. Re-run with " +
167
+ "DEBUG=tamagui to see the underlying bundling error.",
168
+ );
169
+ }
170
+ const artifact = artifacts.sort(
171
+ (a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs,
172
+ )[0]!;
173
+ const source = fs.readFileSync(artifact, "utf8");
174
+ const requireFromArtifact = createRequire(artifact);
175
+ const importRe = /(?:require\s*\(\s*|import\s*\(\s*|from\s*|import\s*)["']([^"'\n]+)["']/g;
176
+ const missing = new Set<string>();
177
+ for (const match of source.matchAll(importRe)) {
178
+ const packageName = barePackageName(match[1]!);
179
+ if (!packageName) continue;
180
+ if (artifact.endsWith(".cjs")) {
181
+ try {
182
+ requireFromArtifact.resolve(packageName);
183
+ } catch {
184
+ missing.add(packageName);
185
+ }
186
+ } else if (!packageVisibleFromDir(packageName, dotDir)) {
187
+ missing.add(packageName);
188
+ }
189
+ }
190
+ if (missing.size > 0) {
191
+ const names = [...missing].map((name) => `"${name}"`).join(", ");
192
+ throw new Error(
193
+ `[${PLUGIN_NAME}] Tamagui config bundling failed: ${names} cannot be resolved ` +
194
+ `from ${artifact}. The Tamagui static extractor swallows this ("Error bundling ` +
195
+ 'tamagui config") and ships a deoptimized runtime-styled build. Fix: add ' +
196
+ `${names} to your app's dependencies — under pnpm's default isolated ` +
197
+ "node_modules, transitive packages are not resolvable from the app.",
198
+ );
199
+ }
200
+ },
201
+ };
202
+ }
203
+
106
204
  function resolveDefaults(options: WebextTargetOptions) {
205
+ const workspaceRootInferred = options.workspaceRoot == null;
107
206
  const workspaceRoot = options.workspaceRoot ?? lookupProjectRoot();
108
207
  const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
109
208
  const port = options.port ?? (Number(process.env.PORT) || 3303);
110
- return { workspaceRoot, isDev, port };
209
+ return { workspaceRoot, workspaceRootInferred, isDev, port };
210
+ }
211
+
212
+ /** Peers stay lazy so requiring them is deferred to the factory that needs
213
+ * them — but a missing peer must name itself, not crash the config file. */
214
+ async function importPeer<T>(load: () => Promise<T>, specifier: string): Promise<T> {
215
+ try {
216
+ return await load();
217
+ } catch (err) {
218
+ throw new Error(
219
+ `[${PLUGIN_NAME}] could not import its peer dependency "${specifier}" — install it ` +
220
+ `next to ${PLUGIN_NAME} (it is a required peer): ${err instanceof Error ? err.message : err}`,
221
+ );
222
+ }
111
223
  }
112
224
 
113
225
  function baseDefine(options: WebextTargetOptions, isDev: boolean): Record<string, unknown> {
@@ -120,10 +232,29 @@ function baseDefine(options: WebextTargetOptions, isDev: boolean): Record<string
120
232
  };
121
233
  }
122
234
 
123
- function baseResolve(options: WebextTargetOptions, workspaceRoot: string) {
235
+ const loggedNoPublicRoots = new Set<string>();
236
+
237
+ function baseResolve(
238
+ options: WebextTargetOptions,
239
+ workspaceRoot: string,
240
+ workspaceRootInferred: boolean,
241
+ ) {
242
+ const sourceAliases = workspaceSourceAliases(workspaceRoot);
243
+ // Out-of-repo consumers have no public/ checkout, so the in-repo source
244
+ // aliasing silently no-ops — say so once, to make the divergence visible.
245
+ if (
246
+ workspaceRootInferred &&
247
+ Object.keys(sourceAliases).length === 0 &&
248
+ !loggedNoPublicRoots.has(workspaceRoot)
249
+ ) {
250
+ loggedNoPublicRoots.add(workspaceRoot);
251
+ logger.info(
252
+ `[${PLUGIN_NAME}] no public/ under ${workspaceRoot} — resolving @multiplatform.one/* from node_modules`,
253
+ );
254
+ }
124
255
  return {
125
256
  alias: [
126
- ...Object.entries(workspaceSourceAliases(workspaceRoot)).map(([find, replacement]) => ({
257
+ ...Object.entries(sourceAliases).map(([find, replacement]) => ({
127
258
  find,
128
259
  replacement,
129
260
  })),
@@ -159,8 +290,17 @@ export function listWebextViews(targetDir: string): string[] {
159
290
 
160
291
  /** The extension pages build (popup/options/sidepanel/devtools…). */
161
292
  export async function createWebextViewsConfig(options: WebextTargetOptions): Promise<UserConfig> {
162
- const { workspaceRoot, isDev, port } = resolveDefaults(options);
163
- const { tamaguiPlugin } = await import("@tamagui/vite-plugin");
293
+ const { workspaceRoot, workspaceRootInferred, isDev, port } = resolveDefaults(options);
294
+ // Both vite-integration peers load lazily at factory-call time so a consumer
295
+ // following the peer contract sees a named error, not a module-scope crash.
296
+ const { default: react, reactCompilerPreset } = await importPeer(
297
+ () => import("@vitejs/plugin-react"),
298
+ "@vitejs/plugin-react",
299
+ );
300
+ const { tamaguiPlugin } = await importPeer(
301
+ () => import("@tamagui/vite-plugin"),
302
+ "@tamagui/vite-plugin",
303
+ );
164
304
  const views = listWebextViews(options.targetDir);
165
305
  return {
166
306
  root: options.targetDir,
@@ -169,11 +309,15 @@ export async function createWebextViewsConfig(options: WebextTargetOptions): Pro
169
309
  stubExpoTypeDeclarations(),
170
310
  oneServerOnlyBrowserStub(workspaceRoot),
171
311
  react({ jsxRuntime: "automatic" }),
312
+ // Same React Compiler pass the One app runs (react 19 built-in
313
+ // runtime) — extension views get the memoization for free.
314
+ babel({ presets: [reactCompilerPreset()] }),
172
315
  tamaguiPlugin({
173
316
  components: lookupTamaguiModules([options.targetDir]),
174
317
  config: options.tamaguiConfig,
175
318
  outputCSS: path.join(options.targetDir, "tamagui.css"),
176
319
  }) as Plugin,
320
+ tamaguiConfigBundleGuard(isDev),
177
321
  ],
178
322
  define: baseDefine(options, isDev),
179
323
  optimizeDeps: baseOptimizeDeps(),
@@ -181,7 +325,7 @@ export async function createWebextViewsConfig(options: WebextTargetOptions): Pro
181
325
  jsx: "automatic",
182
326
  target: "esnext",
183
327
  },
184
- resolve: baseResolve(options, workspaceRoot),
328
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
185
329
  server: {
186
330
  port,
187
331
  hmr: {
@@ -215,7 +359,7 @@ export async function createWebextViewsConfig(options: WebextTargetOptions): Pro
215
359
  /** The background service-worker (chromium) / background-script (firefox)
216
360
  * build — a single-file iife, no DOM. */
217
361
  export function createWebextBackgroundConfig(options: WebextTargetOptions): UserConfig {
218
- const { workspaceRoot, isDev } = resolveDefaults(options);
362
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
219
363
  return {
220
364
  root: options.targetDir,
221
365
  plugins: [oneServerOnlyBrowserStub(workspaceRoot)],
@@ -225,7 +369,7 @@ export function createWebextBackgroundConfig(options: WebextTargetOptions): User
225
369
  jsx: "automatic",
226
370
  target: "esnext",
227
371
  },
228
- resolve: baseResolve(options, workspaceRoot),
372
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
229
373
  build: {
230
374
  watch: isDev ? {} : undefined,
231
375
  outDir: path.join(options.stagingDir, "dist/background"),
@@ -250,19 +394,31 @@ export function createWebextBackgroundConfig(options: WebextTargetOptions): User
250
394
  /** The content-script build — a single-file iife injected into pages, with
251
395
  * Tamagui CSS extracted separately (webext.css web-accessible resource). */
252
396
  export async function createWebextContentConfig(options: WebextTargetOptions): Promise<UserConfig> {
253
- const { workspaceRoot, isDev } = resolveDefaults(options);
254
- const { tamaguiPlugin } = await import("@tamagui/vite-plugin");
397
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
398
+ const { default: react, reactCompilerPreset } = await importPeer(
399
+ () => import("@vitejs/plugin-react"),
400
+ "@vitejs/plugin-react",
401
+ );
402
+ const { tamaguiPlugin } = await importPeer(
403
+ () => import("@tamagui/vite-plugin"),
404
+ "@tamagui/vite-plugin",
405
+ );
255
406
  return {
256
407
  root: options.targetDir,
257
408
  plugins: [
258
409
  stubExpoTypeDeclarations(),
259
410
  oneServerOnlyBrowserStub(workspaceRoot),
260
411
  shikiStubPlugin(),
412
+ // The react plugin transforms JSX (the esbuild jsx setting below
413
+ // stays for non-plugin paths); the babel pass adds the compiler.
414
+ react({ jsxRuntime: "automatic" }),
415
+ babel({ presets: [reactCompilerPreset()] }),
261
416
  tamaguiPlugin({
262
417
  components: lookupTamaguiModules([options.targetDir]),
263
418
  config: options.tamaguiConfig,
264
419
  outputCSS: path.join(options.targetDir, "tamagui.content.css"),
265
420
  }) as Plugin,
421
+ tamaguiConfigBundleGuard(isDev),
266
422
  ],
267
423
  define: {
268
424
  ...baseDefine(options, isDev),
@@ -277,7 +433,7 @@ export async function createWebextContentConfig(options: WebextTargetOptions): P
277
433
  jsx: "automatic",
278
434
  target: "esnext",
279
435
  },
280
- resolve: baseResolve(options, workspaceRoot),
436
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
281
437
  build: {
282
438
  watch: isDev ? {} : undefined,
283
439
  outDir: path.join(options.stagingDir, "dist/contentScripts"),
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC;0CACsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB;2EACuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,WAAW,EAAE,iBAAiB,CAAC;IAC/B,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;qCACiC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;CACnB;AA4BD;;;iDAGiD;AACjD,wBAAgB,eAAe,IAAI,MAAM,CAYxC;AA6CD;2EAC2E;AAC3E,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAO3D;AAED,qEAAqE;AACrE,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAoD/F;AAED;0CAC0C;AAC1C,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,CA+BrF;AAED;6EAC6E;AAC7E,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAqDjG;AAID,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;uEAEmE;IACnE,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC5C;2DACuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;sCAEsC;AACtC,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtF;AAED;;mDAEmD;AACnD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBtF;AAED,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB3F;AAED;uBACuB;AACvB,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBnF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAMtD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC;0CACsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB;2EACuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,WAAW,EAAE,iBAAiB,CAAC;IAC/B,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;qCACiC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;CACnB;AA4BD;;;iDAGiD;AACjD,wBAAgB,eAAe,IAAI,MAAM,CAYxC;AA2KD;2EAC2E;AAC3E,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAO3D;AAED,qEAAqE;AACrE,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiE/F;AAED;0CAC0C;AAC1C,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,CA+BrF;AAED;6EAC6E;AAC7E,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiEjG;AAID,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;uEAEmE;IACnE,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC5C;2DACuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;sCAEsC;AACtC,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtF;AAED;;mDAEmD;AACnD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBtF;AAED,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB3F;AAED;uBACuB;AACvB,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBnF"}