@multiplatform.one/vite-plugin-webext 6.7.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,15 +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
35
  let _rolldown_plugin_babel = require("@rolldown/plugin-babel");
35
36
  _rolldown_plugin_babel = __toESM(_rolldown_plugin_babel, 1);
36
- let _vitejs_plugin_react = require("@vitejs/plugin-react");
37
- _vitejs_plugin_react = __toESM(_vitejs_plugin_react, 1);
38
37
 
39
38
  //#region src/index.ts
39
+ const PLUGIN_NAME = "@multiplatform.one/vite-plugin-webext";
40
+ const logger = console;
40
41
  const WEBEXT_EXTENSIONS = [
41
42
  ".webext.ts",
42
43
  ".webext.tsx",
@@ -78,13 +79,91 @@ function shikiStubPlugin() {
78
79
  }
79
80
  };
80
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
+ }
81
149
  function resolveDefaults(options) {
150
+ const workspaceRootInferred = options.workspaceRoot == null;
82
151
  return {
83
152
  workspaceRoot: options.workspaceRoot ?? (0, _multiplatform_one_utils_dev.lookupProjectRoot)(),
153
+ workspaceRootInferred,
84
154
  isDev: options.isDev ?? process.env.NODE_ENV !== "production",
85
155
  port: options.port ?? (Number(process.env.PORT) || 3303)
86
156
  };
87
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
+ }
88
167
  function baseDefine(options, isDev) {
89
168
  return {
90
169
  __DEV__: isDev,
@@ -94,10 +173,16 @@ function baseDefine(options, isDev) {
94
173
  ...options.define
95
174
  };
96
175
  }
97
- 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
+ }
98
183
  return {
99
184
  alias: [
100
- ...Object.entries((0, _multiplatform_one_utils_dev.workspaceSourceAliases)(workspaceRoot)).map(([find, replacement]) => ({
185
+ ...Object.entries(sourceAliases).map(([find, replacement]) => ({
101
186
  find,
102
187
  replacement
103
188
  })),
@@ -137,8 +222,9 @@ function listWebextViews(targetDir) {
137
222
  }
138
223
  /** The extension pages build (popup/options/sidepanel/devtools…). */
139
224
  async function createWebextViewsConfig(options) {
140
- const { workspaceRoot, isDev, port } = resolveDefaults(options);
141
- 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");
142
228
  const views = listWebextViews(options.targetDir);
143
229
  return {
144
230
  root: options.targetDir,
@@ -146,13 +232,14 @@ async function createWebextViewsConfig(options) {
146
232
  plugins: [
147
233
  (0, _multiplatform_one_utils_dev.stubExpoTypeDeclarations)(),
148
234
  (0, _multiplatform_one_utils_dev.oneServerOnlyBrowserStub)(workspaceRoot),
149
- (0, _vitejs_plugin_react.default)({ jsxRuntime: "automatic" }),
150
- (0, _rolldown_plugin_babel.default)({ presets: [(0, _vitejs_plugin_react.reactCompilerPreset)()] }),
235
+ react({ jsxRuntime: "automatic" }),
236
+ (0, _rolldown_plugin_babel.default)({ presets: [reactCompilerPreset()] }),
151
237
  tamaguiPlugin({
152
238
  components: (0, _multiplatform_one_utils_dev.lookupTamaguiModules)([options.targetDir]),
153
239
  config: options.tamaguiConfig,
154
240
  outputCSS: node_path.default.join(options.targetDir, "tamagui.css")
155
- })
241
+ }),
242
+ tamaguiConfigBundleGuard(isDev)
156
243
  ],
157
244
  define: baseDefine(options, isDev),
158
245
  optimizeDeps: baseOptimizeDeps(),
@@ -160,7 +247,7 @@ async function createWebextViewsConfig(options) {
160
247
  jsx: "automatic",
161
248
  target: "esnext"
162
249
  },
163
- resolve: baseResolve(options, workspaceRoot),
250
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
164
251
  server: {
165
252
  port,
166
253
  hmr: {
@@ -185,7 +272,7 @@ async function createWebextViewsConfig(options) {
185
272
  /** The background service-worker (chromium) / background-script (firefox)
186
273
  * build — a single-file iife, no DOM. */
187
274
  function createWebextBackgroundConfig(options) {
188
- const { workspaceRoot, isDev } = resolveDefaults(options);
275
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
189
276
  return {
190
277
  root: options.targetDir,
191
278
  plugins: [(0, _multiplatform_one_utils_dev.oneServerOnlyBrowserStub)(workspaceRoot)],
@@ -195,7 +282,7 @@ function createWebextBackgroundConfig(options) {
195
282
  jsx: "automatic",
196
283
  target: "esnext"
197
284
  },
198
- resolve: baseResolve(options, workspaceRoot),
285
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
199
286
  build: {
200
287
  watch: isDev ? {} : void 0,
201
288
  outDir: node_path.default.join(options.stagingDir, "dist/background"),
@@ -217,21 +304,23 @@ function createWebextBackgroundConfig(options) {
217
304
  /** The content-script build — a single-file iife injected into pages, with
218
305
  * Tamagui CSS extracted separately (webext.css web-accessible resource). */
219
306
  async function createWebextContentConfig(options) {
220
- const { workspaceRoot, isDev } = resolveDefaults(options);
221
- 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");
222
310
  return {
223
311
  root: options.targetDir,
224
312
  plugins: [
225
313
  (0, _multiplatform_one_utils_dev.stubExpoTypeDeclarations)(),
226
314
  (0, _multiplatform_one_utils_dev.oneServerOnlyBrowserStub)(workspaceRoot),
227
315
  shikiStubPlugin(),
228
- (0, _vitejs_plugin_react.default)({ jsxRuntime: "automatic" }),
229
- (0, _rolldown_plugin_babel.default)({ presets: [(0, _vitejs_plugin_react.reactCompilerPreset)()] }),
316
+ react({ jsxRuntime: "automatic" }),
317
+ (0, _rolldown_plugin_babel.default)({ presets: [reactCompilerPreset()] }),
230
318
  tamaguiPlugin({
231
319
  components: (0, _multiplatform_one_utils_dev.lookupTamaguiModules)([options.targetDir]),
232
320
  config: options.tamaguiConfig,
233
321
  outputCSS: node_path.default.join(options.targetDir, "tamagui.content.css")
234
- })
322
+ }),
323
+ tamaguiConfigBundleGuard(isDev)
235
324
  ],
236
325
  define: {
237
326
  ...baseDefine(options, isDev),
@@ -242,7 +331,7 @@ async function createWebextContentConfig(options) {
242
331
  jsx: "automatic",
243
332
  target: "esnext"
244
333
  },
245
- resolve: baseResolve(options, workspaceRoot),
334
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
246
335
  build: {
247
336
  watch: isDev ? {} : void 0,
248
337
  outDir: node_path.default.join(options.stagingDir, "dist/contentScripts"),
package/lib/index.mjs CHANGED
@@ -1,10 +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
5
  import babel from "@rolldown/plugin-babel";
5
- import react, { reactCompilerPreset } from "@vitejs/plugin-react";
6
6
 
7
7
  //#region src/index.ts
8
+ const PLUGIN_NAME = "@multiplatform.one/vite-plugin-webext";
9
+ const logger = console;
8
10
  const WEBEXT_EXTENSIONS = [
9
11
  ".webext.ts",
10
12
  ".webext.tsx",
@@ -46,13 +48,91 @@ function shikiStubPlugin() {
46
48
  }
47
49
  };
48
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
+ }
49
118
  function resolveDefaults(options) {
119
+ const workspaceRootInferred = options.workspaceRoot == null;
50
120
  return {
51
121
  workspaceRoot: options.workspaceRoot ?? lookupProjectRoot(),
122
+ workspaceRootInferred,
52
123
  isDev: options.isDev ?? process.env.NODE_ENV !== "production",
53
124
  port: options.port ?? (Number(process.env.PORT) || 3303)
54
125
  };
55
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
+ }
56
136
  function baseDefine(options, isDev) {
57
137
  return {
58
138
  __DEV__: isDev,
@@ -62,10 +142,16 @@ function baseDefine(options, isDev) {
62
142
  ...options.define
63
143
  };
64
144
  }
65
- 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
+ }
66
152
  return {
67
153
  alias: [
68
- ...Object.entries(workspaceSourceAliases(workspaceRoot)).map(([find, replacement]) => ({
154
+ ...Object.entries(sourceAliases).map(([find, replacement]) => ({
69
155
  find,
70
156
  replacement
71
157
  })),
@@ -105,8 +191,9 @@ function listWebextViews(targetDir) {
105
191
  }
106
192
  /** The extension pages build (popup/options/sidepanel/devtools…). */
107
193
  async function createWebextViewsConfig(options) {
108
- const { workspaceRoot, isDev, port } = resolveDefaults(options);
109
- 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");
110
197
  const views = listWebextViews(options.targetDir);
111
198
  return {
112
199
  root: options.targetDir,
@@ -120,7 +207,8 @@ async function createWebextViewsConfig(options) {
120
207
  components: lookupTamaguiModules([options.targetDir]),
121
208
  config: options.tamaguiConfig,
122
209
  outputCSS: path.join(options.targetDir, "tamagui.css")
123
- })
210
+ }),
211
+ tamaguiConfigBundleGuard(isDev)
124
212
  ],
125
213
  define: baseDefine(options, isDev),
126
214
  optimizeDeps: baseOptimizeDeps(),
@@ -128,7 +216,7 @@ async function createWebextViewsConfig(options) {
128
216
  jsx: "automatic",
129
217
  target: "esnext"
130
218
  },
131
- resolve: baseResolve(options, workspaceRoot),
219
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
132
220
  server: {
133
221
  port,
134
222
  hmr: {
@@ -153,7 +241,7 @@ async function createWebextViewsConfig(options) {
153
241
  /** The background service-worker (chromium) / background-script (firefox)
154
242
  * build — a single-file iife, no DOM. */
155
243
  function createWebextBackgroundConfig(options) {
156
- const { workspaceRoot, isDev } = resolveDefaults(options);
244
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
157
245
  return {
158
246
  root: options.targetDir,
159
247
  plugins: [oneServerOnlyBrowserStub(workspaceRoot)],
@@ -163,7 +251,7 @@ function createWebextBackgroundConfig(options) {
163
251
  jsx: "automatic",
164
252
  target: "esnext"
165
253
  },
166
- resolve: baseResolve(options, workspaceRoot),
254
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
167
255
  build: {
168
256
  watch: isDev ? {} : void 0,
169
257
  outDir: path.join(options.stagingDir, "dist/background"),
@@ -185,8 +273,9 @@ function createWebextBackgroundConfig(options) {
185
273
  /** The content-script build — a single-file iife injected into pages, with
186
274
  * Tamagui CSS extracted separately (webext.css web-accessible resource). */
187
275
  async function createWebextContentConfig(options) {
188
- const { workspaceRoot, isDev } = resolveDefaults(options);
189
- 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");
190
279
  return {
191
280
  root: options.targetDir,
192
281
  plugins: [
@@ -199,7 +288,8 @@ async function createWebextContentConfig(options) {
199
288
  components: lookupTamaguiModules([options.targetDir]),
200
289
  config: options.tamaguiConfig,
201
290
  outputCSS: path.join(options.targetDir, "tamagui.content.css")
202
- })
291
+ }),
292
+ tamaguiConfigBundleGuard(isDev)
203
293
  ],
204
294
  define: {
205
295
  ...baseDefine(options, isDev),
@@ -210,7 +300,7 @@ async function createWebextContentConfig(options) {
210
300
  jsx: "automatic",
211
301
  target: "esnext"
212
302
  },
213
- resolve: baseResolve(options, workspaceRoot),
303
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
214
304
  build: {
215
305
  watch: isDev ? {} : void 0,
216
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.7.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",
@@ -50,10 +50,10 @@
50
50
  "@rolldown/plugin-babel": "^0.2.3",
51
51
  "babel-plugin-react-compiler": "^1.0.0",
52
52
  "chokidar": "^5.0.0",
53
- "@multiplatform.one/utils": "6.7.0"
53
+ "@multiplatform.one/utils": "7.0.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@tamagui/vite-plugin": "2.0.0-rc.41",
56
+ "@tamagui/vite-plugin": "2.7.6",
57
57
  "@types/babel__core": "^7.20.5",
58
58
  "@types/node": "^25.6.0",
59
59
  "@vitejs/plugin-react": "^6.0.1",
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,
@@ -26,9 +27,12 @@ import {
26
27
  lookupProjectRoot,
27
28
  } from "@multiplatform.one/utils/dev";
28
29
  import babel from "@rolldown/plugin-babel";
29
- import react, { reactCompilerPreset } from "@vitejs/plugin-react";
30
30
  import type { Alias, Plugin, UserConfig } from "vite";
31
31
 
32
+ const PLUGIN_NAME = "@multiplatform.one/vite-plugin-webext";
33
+
34
+ const logger = console;
35
+
32
36
  export interface WebextPackageInfo {
33
37
  name: string;
34
38
  displayName?: string;
@@ -104,11 +108,118 @@ export function shikiStubPlugin(): Plugin {
104
108
  };
105
109
  }
106
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
+
107
204
  function resolveDefaults(options: WebextTargetOptions) {
205
+ const workspaceRootInferred = options.workspaceRoot == null;
108
206
  const workspaceRoot = options.workspaceRoot ?? lookupProjectRoot();
109
207
  const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
110
208
  const port = options.port ?? (Number(process.env.PORT) || 3303);
111
- 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
+ }
112
223
  }
113
224
 
114
225
  function baseDefine(options: WebextTargetOptions, isDev: boolean): Record<string, unknown> {
@@ -121,10 +232,29 @@ function baseDefine(options: WebextTargetOptions, isDev: boolean): Record<string
121
232
  };
122
233
  }
123
234
 
124
- 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
+ }
125
255
  return {
126
256
  alias: [
127
- ...Object.entries(workspaceSourceAliases(workspaceRoot)).map(([find, replacement]) => ({
257
+ ...Object.entries(sourceAliases).map(([find, replacement]) => ({
128
258
  find,
129
259
  replacement,
130
260
  })),
@@ -160,8 +290,17 @@ export function listWebextViews(targetDir: string): string[] {
160
290
 
161
291
  /** The extension pages build (popup/options/sidepanel/devtools…). */
162
292
  export async function createWebextViewsConfig(options: WebextTargetOptions): Promise<UserConfig> {
163
- const { workspaceRoot, isDev, port } = resolveDefaults(options);
164
- 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
+ );
165
304
  const views = listWebextViews(options.targetDir);
166
305
  return {
167
306
  root: options.targetDir,
@@ -178,6 +317,7 @@ export async function createWebextViewsConfig(options: WebextTargetOptions): Pro
178
317
  config: options.tamaguiConfig,
179
318
  outputCSS: path.join(options.targetDir, "tamagui.css"),
180
319
  }) as Plugin,
320
+ tamaguiConfigBundleGuard(isDev),
181
321
  ],
182
322
  define: baseDefine(options, isDev),
183
323
  optimizeDeps: baseOptimizeDeps(),
@@ -185,7 +325,7 @@ export async function createWebextViewsConfig(options: WebextTargetOptions): Pro
185
325
  jsx: "automatic",
186
326
  target: "esnext",
187
327
  },
188
- resolve: baseResolve(options, workspaceRoot),
328
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
189
329
  server: {
190
330
  port,
191
331
  hmr: {
@@ -219,7 +359,7 @@ export async function createWebextViewsConfig(options: WebextTargetOptions): Pro
219
359
  /** The background service-worker (chromium) / background-script (firefox)
220
360
  * build — a single-file iife, no DOM. */
221
361
  export function createWebextBackgroundConfig(options: WebextTargetOptions): UserConfig {
222
- const { workspaceRoot, isDev } = resolveDefaults(options);
362
+ const { workspaceRoot, workspaceRootInferred, isDev } = resolveDefaults(options);
223
363
  return {
224
364
  root: options.targetDir,
225
365
  plugins: [oneServerOnlyBrowserStub(workspaceRoot)],
@@ -229,7 +369,7 @@ export function createWebextBackgroundConfig(options: WebextTargetOptions): User
229
369
  jsx: "automatic",
230
370
  target: "esnext",
231
371
  },
232
- resolve: baseResolve(options, workspaceRoot),
372
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
233
373
  build: {
234
374
  watch: isDev ? {} : undefined,
235
375
  outDir: path.join(options.stagingDir, "dist/background"),
@@ -254,8 +394,15 @@ export function createWebextBackgroundConfig(options: WebextTargetOptions): User
254
394
  /** The content-script build — a single-file iife injected into pages, with
255
395
  * Tamagui CSS extracted separately (webext.css web-accessible resource). */
256
396
  export async function createWebextContentConfig(options: WebextTargetOptions): Promise<UserConfig> {
257
- const { workspaceRoot, isDev } = resolveDefaults(options);
258
- 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
+ );
259
406
  return {
260
407
  root: options.targetDir,
261
408
  plugins: [
@@ -271,6 +418,7 @@ export async function createWebextContentConfig(options: WebextTargetOptions): P
271
418
  config: options.tamaguiConfig,
272
419
  outputCSS: path.join(options.targetDir, "tamagui.content.css"),
273
420
  }) as Plugin,
421
+ tamaguiConfigBundleGuard(isDev),
274
422
  ],
275
423
  define: {
276
424
  ...baseDefine(options, isDev),
@@ -285,7 +433,7 @@ export async function createWebextContentConfig(options: WebextTargetOptions): P
285
433
  jsx: "automatic",
286
434
  target: "esnext",
287
435
  },
288
- resolve: baseResolve(options, workspaceRoot),
436
+ resolve: baseResolve(options, workspaceRoot, workspaceRootInferred),
289
437
  build: {
290
438
  watch: isDev ? {} : undefined,
291
439
  outDir: path.join(options.stagingDir, "dist/contentScripts"),
@@ -1 +1 @@
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;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,CAuD/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,CAyDjG;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"}