@crxjs/vite-plugin 2.3.0 → 2.4.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/dist/index.d.ts CHANGED
@@ -203,7 +203,29 @@ interface ManifestV3 {
203
203
  update_url?: string | undefined;
204
204
  version_name?: string | undefined;
205
205
  web_accessible_resources?: (WebAccessibleResourceById | WebAccessibleResourceByMatch)[] | undefined;
206
+ browser_specific_settings?: {
207
+ gecko: {
208
+ id: string;
209
+ strict_min_version?: string | undefined;
210
+ strict_max_version?: string | undefined;
211
+ update_url?: string | undefined;
212
+ data_collection_permissions: {
213
+ /**
214
+ * available value: "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "none".
215
+ * see also: https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/
216
+ */
217
+ required: GeckoPermissionsRequired[];
218
+ /**
219
+ * available value: "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "technicalAndInteraction".
220
+ * see also: https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/
221
+ */
222
+ optional?: GeckoPermissionsOptional[] | undefined;
223
+ };
224
+ };
225
+ } | undefined;
206
226
  }
227
+ type GeckoPermissionsRequired = "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "none";
228
+ type GeckoPermissionsOptional = "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "technicalAndInteraction";
207
229
 
208
230
  type ManifestV3Fn = (env: ConfigEnv) => ManifestV3 | Promise<ManifestV3>;
209
231
  type ManifestV3Export = ManifestV3 | Promise<ManifestV3> | ManifestV3Fn;
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { simple } from 'acorn-walk';
2
2
  import { createHash } from 'crypto';
3
- import debug$3 from 'debug';
4
- import { join, normalize, isAbsolute, basename, relative, resolve, dirname, parse } from 'pathe';
3
+ import debug$5 from 'debug';
4
+ import { join, normalize, dirname, basename, isAbsolute, relative, resolve, parse as parse$1 } from 'pathe';
5
5
  import { Subject, filter, ReplaySubject, switchMap, of, startWith, map, BehaviorSubject, mergeMap, firstValueFrom, takeUntil, first, toArray, retry, concatWith, Subscription, buffer } from 'rxjs';
6
6
  import fsx from 'fs-extra';
7
7
  import { performance } from 'perf_hooks';
@@ -15,7 +15,7 @@ import { createLogger, version } from 'vite';
15
15
  import { readFileSync, existsSync, promises } from 'fs';
16
16
  import { createRequire } from 'module';
17
17
  import fg from 'fast-glob';
18
- import { load } from 'cheerio';
18
+ import { parse } from 'node-html-parser';
19
19
  import jsesc from 'jsesc';
20
20
 
21
21
  const pluginName$1 = "crx:optionsProvider";
@@ -58,9 +58,9 @@ function isCrxPlugin(p) {
58
58
  return !!p && typeof p === "object" && !(p instanceof Promise) && !Array.isArray(p) && p.name.startsWith("crx:");
59
59
  }
60
60
 
61
- var workerHmrClient = "const ownOrigin = `chrome-extension://${chrome.runtime.id}`;\nself.addEventListener(\"fetch\", (fetchEvent) => {\n const url = new URL(fetchEvent.request.url);\n if (url.origin === ownOrigin) {\n fetchEvent.respondWith(sendToServer(fetchEvent.request));\n }\n});\nasync function sendToServer(req) {\n const url = new URL(req.url);\n const requestHeaders = new Headers(req.headers);\n url.protocol = \"http:\";\n url.host = \"localhost\";\n url.port = __SERVER_PORT__;\n url.searchParams.set(\"t\", Date.now().toString());\n const response = await fetch(url.href.replace(/=$|=(?=&)/g, \"\"), {\n headers: requestHeaders\n });\n const responseHeaders = new Headers(response.headers);\n responseHeaders.set(\"Content-Type\", responseHeaders.get(\"Content-Type\") ?? \"text/javascript\");\n responseHeaders.set(\"Cache-Control\", responseHeaders.get(\"Cache-Control\") ?? \"\");\n return new Response(response.body, {\n headers: responseHeaders\n });\n}\nconst ports = /* @__PURE__ */ new Set();\nchrome.runtime.onConnect.addListener((port) => {\n if (port.name === \"@crx/client\") {\n ports.add(port);\n port.onDisconnect.addListener((port2) => {\n if (chrome.runtime.lastError) {\n console.error(chrome.runtime.lastError);\n }\n ports.delete(port2);\n });\n port.onMessage.addListener((message) => {\n });\n port.postMessage({ data: JSON.stringify({ type: \"connected\" }) });\n }\n});\nfunction notifyContentScripts(payload) {\n const data = JSON.stringify(payload);\n for (const port of ports)\n port.postMessage({ data });\n}\nconsole.log(\"[vite] connecting...\");\nconst socketProtocol = __HMR_PROTOCOL__ || (location.protocol === \"https:\" ? \"wss\" : \"ws\");\nconst socketToken = __HMR_TOKEN__;\nconst socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`;\nconst socket = new WebSocket(`${socketProtocol}://${socketHost}?token=${socketToken}`, \"vite-hmr\");\nconst base = __BASE__ || \"/\";\nsocket.addEventListener(\"message\", async ({ data }) => {\n handleSocketMessage(JSON.parse(data));\n});\nfunction isCrxHmrPayload(x) {\n return x.type === \"custom\" && x.event.startsWith(\"crx:\");\n}\nfunction handleSocketMessage(payload) {\n if (isCrxHmrPayload(payload)) {\n handleCrxHmrPayload(payload);\n } else if (payload.type === \"connected\") {\n console.log(`[vite] connected.`);\n const interval = setInterval(() => socket.send(\"ping\"), __HMR_TIMEOUT__);\n socket.addEventListener(\"close\", () => clearInterval(interval));\n }\n}\nfunction handleCrxHmrPayload(payload) {\n notifyContentScripts(payload);\n switch (payload.event) {\n case \"crx:runtime-reload\":\n console.log(\"[crx] runtime reload\");\n chrome.runtime.reload();\n break;\n }\n}\nasync function waitForSuccessfulPing(ms = 1e3) {\n while (true) {\n try {\n await fetch(`${base}__vite_ping`);\n break;\n } catch (e) {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n }\n}\nsocket.addEventListener(\"close\", async ({ wasClean }) => {\n if (wasClean)\n return;\n console.log(`[vite] server connection lost. polling for restart...`);\n await waitForSuccessfulPing();\n handleCrxHmrPayload({\n type: \"custom\",\n event: \"crx:runtime-reload\"\n });\n});\n";
61
+ var workerHmrClient = "const ownOrigin = `chrome-extension://${chrome.runtime.id}`;\nself.addEventListener(\"fetch\", (fetchEvent) => {\n const url = new URL(fetchEvent.request.url);\n if (url.origin === ownOrigin) {\n fetchEvent.respondWith(sendToServer(fetchEvent.request));\n }\n});\nasync function sendToServer(req) {\n const url = new URL(req.url);\n const requestHeaders = new Headers(req.headers);\n url.protocol = __SERVER_PROTO__ + \":\";\n url.host = \"localhost\";\n url.port = __SERVER_PORT__;\n url.searchParams.set(\"t\", Date.now().toString());\n const response = await fetch(url.href.replace(/=$|=(?=&)/g, \"\"), {\n headers: requestHeaders\n });\n const responseHeaders = new Headers(response.headers);\n responseHeaders.set(\n \"Content-Type\",\n responseHeaders.get(\"Content-Type\") ?? \"text/javascript\"\n );\n responseHeaders.set(\n \"Cache-Control\",\n responseHeaders.get(\"Cache-Control\") ?? \"\"\n );\n return new Response(response.body, {\n headers: responseHeaders\n });\n}\nconst ports = /* @__PURE__ */ new Set();\nchrome.runtime.onConnect.addListener((port) => {\n if (port.name === \"@crx/client\") {\n ports.add(port);\n port.onDisconnect.addListener((port2) => {\n if (chrome.runtime.lastError) {\n console.error(chrome.runtime.lastError);\n }\n ports.delete(port2);\n });\n port.onMessage.addListener((message) => {\n });\n port.postMessage({ data: JSON.stringify({ type: \"connected\" }) });\n }\n});\nfunction notifyContentScripts(payload) {\n const data = JSON.stringify(payload);\n for (const port of ports)\n port.postMessage({ data });\n}\nconsole.log(\"[vite] connecting...\");\nconst socketProtocol = __HMR_PROTOCOL__ || (location.protocol === \"https:\" ? \"wss\" : \"ws\");\nconst socketToken = __HMR_TOKEN__;\nconst socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`;\nconst socket = new WebSocket(\n `${socketProtocol}://${socketHost}?token=${socketToken}`,\n \"vite-hmr\"\n);\nconst base = __BASE__ || \"/\";\nsocket.addEventListener(\"message\", async ({ data }) => {\n handleSocketMessage(JSON.parse(data));\n});\nfunction isCrxHmrPayload(x) {\n return x.type === \"custom\" && x.event.startsWith(\"crx:\");\n}\nfunction handleSocketMessage(payload) {\n if (isCrxHmrPayload(payload)) {\n handleCrxHmrPayload(payload);\n } else if (payload.type === \"connected\") {\n console.log(`[vite] connected.`);\n const interval = setInterval(() => socket.send(\"ping\"), __HMR_TIMEOUT__);\n socket.addEventListener(\"close\", () => clearInterval(interval));\n }\n}\nfunction handleCrxHmrPayload(payload) {\n notifyContentScripts(payload);\n switch (payload.event) {\n case \"crx:runtime-reload\":\n console.log(\"[crx] runtime reload\");\n chrome.runtime.reload();\n break;\n }\n}\nasync function waitForSuccessfulPing(ms = 1e3) {\n while (true) {\n try {\n await fetch(`${base}__vite_ping`);\n break;\n } catch (e) {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n }\n}\nsocket.addEventListener(\"close\", async ({ wasClean }) => {\n if (wasClean)\n return;\n console.log(`[vite] server connection lost. polling for restart...`);\n await waitForSuccessfulPing();\n handleCrxHmrPayload({\n type: \"custom\",\n event: \"crx:runtime-reload\"\n });\n});\n";
62
62
 
63
- const _debug = (id) => debug$3("crx").extend(id);
63
+ const _debug = (id) => debug$5("crx").extend(id);
64
64
  const hash = (data, length = 5) => createHash("sha1").update(data).digest("base64").replace(/[^A-Za-z0-9]/g, "").slice(0, length);
65
65
  const isString = (x) => typeof x === "string";
66
66
  function isObject(value) {
@@ -140,7 +140,13 @@ function defineClientValues(code, config) {
140
140
  if (hmrBase !== "/") {
141
141
  hmrPort = normalize(`${hmrPort}${hmrBase}`);
142
142
  }
143
- return code.replace(`__MODE__`, JSON.stringify(config.mode)).replace(`__BASE__`, JSON.stringify(config.base)).replace(`__DEFINES__`, serializeDefine(config.define || {})).replace(`__HMR_TOKEN__`, JSON.stringify(config.webSocketToken || "")).replace(`__HMR_PROTOCOL__`, JSON.stringify(protocol)).replace(`__HMR_HOSTNAME__`, JSON.stringify(host)).replace(`__HMR_PORT__`, JSON.stringify(hmrPort)).replace(`__HMR_TIMEOUT__`, JSON.stringify(timeout)).replace(`__HMR_ENABLE_OVERLAY__`, JSON.stringify(overlay)).replace(`__SERVER_PORT__`, JSON.stringify(config.server.port?.toString()));
143
+ return code.replace(`__MODE__`, JSON.stringify(config.mode)).replace(`__BASE__`, JSON.stringify(config.base)).replace(`__DEFINES__`, serializeDefine(config.define || {})).replace(`__HMR_TOKEN__`, JSON.stringify(config.webSocketToken || "")).replace(`__HMR_PROTOCOL__`, JSON.stringify(protocol)).replace(`__HMR_HOSTNAME__`, JSON.stringify(host)).replace(`__HMR_PORT__`, JSON.stringify(hmrPort)).replace(`__HMR_TIMEOUT__`, JSON.stringify(timeout)).replace(`__HMR_ENABLE_OVERLAY__`, JSON.stringify(overlay)).replace(
144
+ `__SERVER_PROTO__`,
145
+ JSON.stringify(config.server.https ? "https" : "http")
146
+ ).replace(
147
+ `__SERVER_PORT__`,
148
+ JSON.stringify(config.server.port?.toString())
149
+ );
144
150
  function serializeDefine(define) {
145
151
  let res = `{`;
146
152
  for (const key in define) {
@@ -177,6 +183,17 @@ class RxMap extends Map {
177
183
  const outputFiles = new RxMap();
178
184
 
179
185
  _debug("file-writer").extend("utilities");
186
+ function sanitizeUnderscorePrefix(fileName) {
187
+ const dir = dirname(fileName);
188
+ let base = basename(fileName);
189
+ while (base.startsWith("_")) {
190
+ base = base.slice(1);
191
+ }
192
+ if (!base) {
193
+ base = "file";
194
+ }
195
+ return dir === "." ? base : join(dir, base);
196
+ }
180
197
  function prefix$1(prefix2, text) {
181
198
  return text.startsWith(prefix2) ? text : prefix2 + text;
182
199
  }
@@ -200,6 +217,7 @@ function getFileName({ type, id }) {
200
217
  } else if (fileName.startsWith(".vite/deps/")) {
201
218
  fileName = `vendor/${fileName.slice(".vite/deps/".length)}`;
202
219
  }
220
+ fileName = sanitizeUnderscorePrefix(fileName);
203
221
  switch (type) {
204
222
  case "iife":
205
223
  return `${fileName}.iife.js`;
@@ -257,6 +275,20 @@ const manifestId = "/@crx/manifest";
257
275
  const preambleId = "/@crx/client-preamble";
258
276
  const stubId = "/@crx/stub";
259
277
  const workerClientId = "/@crx/client-worker";
278
+ const contentCssPrefix = "/@crx/content-css/";
279
+ function isContentCssId(id) {
280
+ return id.startsWith(contentCssPrefix);
281
+ }
282
+ function getContentCssId(index) {
283
+ return `${contentCssPrefix}${index}`;
284
+ }
285
+ function getContentCssIndex(id) {
286
+ if (!isContentCssId(id))
287
+ return null;
288
+ const indexStr = id.slice(contentCssPrefix.length);
289
+ const index = parseInt(indexStr, 10);
290
+ return isNaN(index) ? null : index;
291
+ }
260
292
 
261
293
  const pluginBackground = () => {
262
294
  let config;
@@ -271,7 +303,7 @@ const pluginBackground = () => {
271
303
  },
272
304
  load(id) {
273
305
  if (id === workerClientId) {
274
- const base = `http://localhost:${config.server.port}/`;
306
+ const base = `${config.server.https ? "https" : "http"}://localhost:${config.server.port}/`;
275
307
  return defineClientValues(
276
308
  workerHmrClient.replace("__BASE__", JSON.stringify(base)),
277
309
  config
@@ -294,24 +326,25 @@ const pluginBackground = () => {
294
326
  const worker = browser === "firefox" ? manifest.background?.scripts[0] : manifest.background?.service_worker;
295
327
  let loader;
296
328
  if (config.command === "serve") {
329
+ const proto = config.server.https ? "https" : "http";
297
330
  const port = config.server.port?.toString();
298
331
  if (typeof port === "undefined")
299
332
  throw new Error("server port is undefined in watch mode");
300
333
  if (browser === "firefox") {
301
- loader = `import('http://localhost:${port}/@vite/env');
334
+ loader = `import('${proto}://localhost:${port}/@vite/env');
302
335
  `;
303
- loader += `import('http://localhost:${port}${workerClientId}');
336
+ loader += `import('${proto}://localhost:${port}${workerClientId}');
304
337
  `;
305
338
  if (worker)
306
- loader += `import('http://localhost:${port}/${worker}');
339
+ loader += `import('${proto}://localhost:${port}/${worker}');
307
340
  `;
308
341
  } else {
309
- loader = `import 'http://localhost:${port}/@vite/env';
342
+ loader = `import '${proto}://localhost:${port}/@vite/env';
310
343
  `;
311
- loader += `import 'http://localhost:${port}${workerClientId}';
344
+ loader += `import '${proto}://localhost:${port}${workerClientId}';
312
345
  `;
313
346
  if (worker)
314
- loader += `import 'http://localhost:${port}/${worker}';
347
+ loader += `import '${proto}://localhost:${port}/${worker}';
315
348
  `;
316
349
  }
317
350
  } else if (worker) {
@@ -515,7 +548,7 @@ async function allFilesReady() {
515
548
  }
516
549
 
517
550
  const { outputFile } = fsx;
518
- _debug("file-writer");
551
+ const debug$4 = _debug("file-writer");
519
552
  async function start({
520
553
  server
521
554
  }) {
@@ -546,6 +579,12 @@ async function close() {
546
579
  }
547
580
  function add(script) {
548
581
  const fileName = getFileName(script);
582
+ debug$4(
583
+ "add: script.id=%s script.type=%s fileName=%s",
584
+ script.id,
585
+ script.type,
586
+ fileName
587
+ );
549
588
  let file = outputFiles.get(fileName);
550
589
  if (typeof file === "undefined") {
551
590
  file = formatFileData({
@@ -554,6 +593,17 @@ function add(script) {
554
593
  file: write(script)
555
594
  });
556
595
  outputFiles.set(file.fileName, file);
596
+ debug$4("add: stored new file %s", file.fileName);
597
+ } else {
598
+ const isVirtualModule = script.id.startsWith("/@id/") || script.id.startsWith("/__");
599
+ if (isVirtualModule) {
600
+ debug$4(
601
+ "add: virtual module already exists, triggering re-write for %s",
602
+ fileName
603
+ );
604
+ file.file = write(script);
605
+ outputFiles.set(fileName, file);
606
+ }
557
607
  }
558
608
  return file;
559
609
  }
@@ -561,15 +611,19 @@ function update(_id) {
561
611
  const id = prefix$1("/", _id);
562
612
  const types = ["iife", "module"];
563
613
  const updatedFiles = [];
614
+ debug$4("update called: _id=%s id=%s", _id, id);
564
615
  for (const type of types) {
565
616
  const fileName = getFileName({ id, type });
617
+ debug$4("update: looking for fileName=%s", fileName);
566
618
  const scriptFile = outputFiles.get(fileName);
567
619
  if (scriptFile) {
620
+ debug$4("update: found file, calling write()");
568
621
  scriptFile.file = write({ id, type });
569
622
  updatedFiles.push(scriptFile);
570
623
  outputFiles.set(fileName, scriptFile);
571
624
  }
572
625
  }
626
+ debug$4("update: returning %d files", updatedFiles.length);
573
627
  return updatedFiles;
574
628
  }
575
629
  async function write(fileId) {
@@ -742,7 +796,9 @@ const pluginContentScripts = () => {
742
796
  type: "loader",
743
797
  id: basename(script.id)
744
798
  }),
745
- source: worldMainIds.has(script.id) ? createProMainLoader({ fileName: `./${fileName.split("/").at(-1)}` }) : createProLoader({ fileName })
799
+ source: worldMainIds.has(script.id) ? createProMainLoader({
800
+ fileName: `./${fileName.split("/").at(-1)}`
801
+ }) : createProLoader({ fileName })
746
802
  });
747
803
  script.loaderName = this.getFileName(refId);
748
804
  } else {
@@ -790,8 +846,48 @@ const pluginContentScriptsCss = () => {
790
846
  };
791
847
  };
792
848
 
849
+ const contentCssEntries = /* @__PURE__ */ new Map();
850
+ function getContentCssEntries() {
851
+ return Array.from(contentCssEntries.values());
852
+ }
853
+ function clearContentCssEntries() {
854
+ contentCssEntries.clear();
855
+ }
856
+ function registerContentCssEntry(index, cssFiles) {
857
+ const virtualId = getContentCssId(index);
858
+ const entry = { index, cssFiles, virtualId };
859
+ contentCssEntries.set(index, entry);
860
+ return entry;
861
+ }
793
862
  const pluginDeclaredContentScripts = () => {
794
- return [];
863
+ return {
864
+ name: "crx:content-scripts-declared-css",
865
+ apply: "serve",
866
+ resolveId(source) {
867
+ if (isContentCssId(source)) {
868
+ return source;
869
+ }
870
+ },
871
+ load(id) {
872
+ if (!isContentCssId(id))
873
+ return;
874
+ const index = getContentCssIndex(id);
875
+ if (index === null)
876
+ return;
877
+ const entry = contentCssEntries.get(index);
878
+ if (!entry) {
879
+ console.warn(
880
+ `[crx:content-scripts-declared-css] No CSS entry found for index ${index}`
881
+ );
882
+ return "";
883
+ }
884
+ const cssImports = entry.cssFiles.map((cssPath) => {
885
+ const importPath = cssPath.startsWith("/") ? cssPath : `/${cssPath}`;
886
+ return `import "${importPath}";`;
887
+ }).join("\n");
888
+ return cssImports + "\n";
889
+ }
890
+ };
795
891
  };
796
892
 
797
893
  const _dynamicScriptRegEx = /\b(import.meta).CRX_DYNAMIC_SCRIPT_(.+?)[,;]/gm;
@@ -1104,7 +1200,7 @@ const pluginFileWriterPublic = () => {
1104
1200
  };
1105
1201
  };
1106
1202
 
1107
- const debug$2 = _debug("file-writer").extend("hmr");
1203
+ const debug$3 = _debug("file-writer").extend("hmr");
1108
1204
  const isCustomPayload = (p) => {
1109
1205
  return p.type === "custom";
1110
1206
  };
@@ -1142,7 +1238,21 @@ const crxHMRPayload$ = hmrPayload$.pipe(
1142
1238
  return prune;
1143
1239
  }
1144
1240
  case "update": {
1145
- const update = {
1241
+ debug$3("update payload with %d updates", p.updates.length);
1242
+ for (const u of p.updates) {
1243
+ debug$3(
1244
+ "update item: path=%s acceptedPath=%s type=%s",
1245
+ u.path,
1246
+ u.acceptedPath,
1247
+ u.type
1248
+ );
1249
+ const isVirtualModule = u.path.startsWith("/@id/") || u.path.startsWith("/__");
1250
+ if (isVirtualModule) {
1251
+ debug$3("updating virtual module: %s", u.path);
1252
+ update(u.path);
1253
+ }
1254
+ }
1255
+ const update_ = {
1146
1256
  type: "update",
1147
1257
  updates: p.updates.map(({ acceptedPath: ap, path: p2, ...rest }) => ({
1148
1258
  ...rest,
@@ -1150,7 +1260,7 @@ const crxHMRPayload$ = hmrPayload$.pipe(
1150
1260
  path: prefix$1("/", getFileName({ id: p2, type: "module" }))
1151
1261
  }))
1152
1262
  };
1153
- return update;
1263
+ return update_;
1154
1264
  }
1155
1265
  default:
1156
1266
  return p;
@@ -1169,7 +1279,7 @@ const crxHMRPayload$ = hmrPayload$.pipe(
1169
1279
  }
1170
1280
  }),
1171
1281
  map((data) => {
1172
- debug$2(`hmr payload`, data);
1282
+ debug$3(`hmr payload`, data);
1173
1283
  return {
1174
1284
  type: "custom",
1175
1285
  event: "crx:content-script-payload",
@@ -1194,7 +1304,7 @@ function isImporter(file) {
1194
1304
  return pred;
1195
1305
  }
1196
1306
 
1197
- const debug$1 = _debug("hmr");
1307
+ const debug$2 = _debug("hmr");
1198
1308
  const crxRuntimeReload = {
1199
1309
  type: "custom",
1200
1310
  event: "crx:runtime-reload"
@@ -1262,25 +1372,51 @@ const pluginHMR = () => {
1262
1372
  const { root } = server.config;
1263
1373
  const relFiles = /* @__PURE__ */ new Set();
1264
1374
  const fsFiles = /* @__PURE__ */ new Set();
1375
+ const virtualModules = /* @__PURE__ */ new Set();
1265
1376
  for (const m of modules) {
1266
1377
  if (m.id?.startsWith(root)) {
1267
1378
  relFiles.add(m.id.slice(server.config.root.length));
1268
1379
  } else if (m.url?.startsWith("/@fs")) {
1269
1380
  fsFiles.add(m.url);
1381
+ } else if (m.id?.startsWith("\0") || m.url?.startsWith("/@id/__x00__")) {
1382
+ const virtualId = m.url ?? m.id;
1383
+ if (virtualId) {
1384
+ virtualModules.add(virtualId);
1385
+ debug$2("virtual module detected:", virtualId);
1386
+ }
1270
1387
  }
1271
1388
  }
1272
1389
  fsFiles.forEach((file) => update(file));
1390
+ virtualModules.forEach((file) => update(file));
1273
1391
  if (inputManifestFiles.background.length) {
1274
1392
  const background = prefix$1("/", inputManifestFiles.background[0]);
1275
1393
  if (relFiles.has(background) || modules.some(isImporter(join(server.config.root, background)))) {
1276
- debug$1("sending runtime reload");
1394
+ debug$2("sending runtime reload");
1277
1395
  server.ws.send(crxRuntimeReload);
1278
1396
  }
1279
1397
  }
1280
1398
  for (const [key, script] of contentScripts)
1281
1399
  if (key === script.id) {
1282
- if (relFiles.has(script.id) || modules.some(isImporter(join(server.config.root, script.id)))) {
1283
- relFiles.forEach((relFile) => update(relFile));
1400
+ if (isContentCssId(script.id)) {
1401
+ const cssEntries = getContentCssEntries();
1402
+ const entry = cssEntries.find((e) => e.virtualId === script.id);
1403
+ if (entry) {
1404
+ const changedCssFiles = [...relFiles].filter(
1405
+ (relFile) => entry.cssFiles.some(
1406
+ (cssFile) => relFile === prefix$1("/", cssFile) || relFile.endsWith(cssFile)
1407
+ )
1408
+ );
1409
+ if (changedCssFiles.length > 0) {
1410
+ changedCssFiles.forEach((relFile) => update(relFile));
1411
+ update(script.id);
1412
+ virtualModules.forEach((file) => update(file));
1413
+ }
1414
+ }
1415
+ } else {
1416
+ if (relFiles.has(script.id) || modules.some(isImporter(join(server.config.root, script.id)))) {
1417
+ relFiles.forEach((relFile) => update(relFile));
1418
+ virtualModules.forEach((file) => update(file));
1419
+ }
1284
1420
  }
1285
1421
  }
1286
1422
  }
@@ -1339,12 +1475,20 @@ const pluginPrint = () => {
1339
1475
 
1340
1476
  var loader = "try {\n for (const p of JSON.parse(SCRIPTS)) {\n const url = new URL(p, \"https://stub\");\n url.searchParams.set(\"t\", Date.now().toString());\n const req = url.pathname + url.search;\n await import(\n /* @vite-ignore */\n req\n );\n }\n} catch (error) {\n console.error(error);\n}\n";
1341
1477
 
1478
+ function extractScriptsAndRemove(html) {
1479
+ const root = parse(html);
1480
+ const scripts = root.querySelectorAll("script");
1481
+ const scriptSrcs = scripts.map((el) => el.getAttribute("src"));
1482
+ scripts.forEach((el) => el.remove());
1483
+ return { scriptSrcs, html: root.toString() };
1484
+ }
1485
+
1342
1486
  const pluginName = "crx:html-inline-scripts";
1343
- const debug = _debug(pluginName);
1487
+ const debug$1 = _debug(pluginName);
1344
1488
  const prefix = "@crx/inline-script";
1345
1489
  const isInlineTag = (t) => t.tag === "script" && !t.attrs?.src;
1346
1490
  const toKey = (ctx) => {
1347
- const { dir, name } = parse(ctx.path);
1491
+ const { dir, name } = parse$1(ctx.path);
1348
1492
  return join(prefix, dir, name);
1349
1493
  };
1350
1494
  const pluginHtmlInlineScripts = () => {
@@ -1410,19 +1554,18 @@ const pluginHtmlInlineScripts = () => {
1410
1554
  const key = toKey(ctx);
1411
1555
  const p = pages.get(key);
1412
1556
  if (p?.scripts.some(isInlineTag)) {
1413
- const $ = load(html);
1557
+ const { scriptSrcs, html: cleanedHtml } = extractScriptsAndRemove(html);
1414
1558
  p.scripts.push(
1415
- ...$("script").toArray().map((el) => ({
1559
+ ...scriptSrcs.map((src) => ({
1416
1560
  tag: "script",
1417
- attrs: { src: $(el).attr("src"), type: "module" }
1561
+ attrs: { src, type: "module" }
1418
1562
  }))
1419
1563
  );
1420
- $("script").remove();
1421
1564
  const loader2 = {
1422
1565
  tag: "script",
1423
1566
  attrs: { src: `${key}?t=${Date.now()}`, type: "module" }
1424
1567
  };
1425
- return { html: $.html(), tags: [loader2] };
1568
+ return { html: cleanedHtml, tags: [loader2] };
1426
1569
  }
1427
1570
  return p?.scripts ?? void 0;
1428
1571
  }
@@ -1464,21 +1607,21 @@ const pluginHtmlInlineScripts = () => {
1464
1607
  })}"`;
1465
1608
  return [inline, loader.replace("SCRIPTS", json)].join("\n");
1466
1609
  } else {
1467
- debug("page missing %s", id);
1610
+ debug$1("page missing %s", id);
1468
1611
  }
1469
1612
  }
1470
1613
  }
1471
1614
  };
1472
1615
  };
1473
1616
 
1474
- var loadingPageScript = "const VITE_URL = \"http://localhost:%PORT%\";\ndocument.body.innerHTML = `\n<div\n id=\"app\"\n style=\"\n border: 1px solid #ddd;\n padding: 20px;\n border-radius: 5px;\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);\n \"\n >\n <h1 style=\"color: #333\">Vite Dev Mode</h1>\n <p style=\"color: #666\">\n Cannot connect to the Vite Dev Server on <a href=\"${VITE_URL}\">${VITE_URL}</a>\n </p>\n <p style=\"color: #666\">\n Double-check that Vite is working and reload the extension.\n </p>\n <p style=\"color: #666\">\n This page will close when the extension reloads.\n </p>\n <button\n style=\"\n padding: 10px 20px;\n border: none;\n background-color: #007bff;\n color: #fff;\n border-radius: 5px;\n cursor: pointer;\n \"\n >\n Reload Extension\n </button>\n </div>`;\ndocument.body.querySelector(\"button\")?.addEventListener(\"click\", () => {\n chrome.runtime.reload();\n});\nlet tries = 0;\nlet ready = false;\ndo {\n try {\n await fetch(VITE_URL);\n ready = true;\n } catch {\n const timeout = Math.min(100 * Math.pow(2, ++tries), 5e3);\n console.log(`[CRXJS] Vite Dev Server is not available on ${VITE_URL}`);\n console.log(`[CRXJS] Retrying in ${timeout}ms...`);\n await new Promise((resolve) => setTimeout(resolve, timeout));\n }\n} while (!ready);\nlocation.reload();\n";
1617
+ var loadingPageScript = "const VITE_URL = \"%PROTO%://localhost:%PORT%\";\ndocument.body.innerHTML = /* html */\n`\n<style>\n :root {\n color-scheme: light;\n --ink: #111827;\n --muted: #5f6b7a;\n --muted-subtle: rgba(95, 107, 122, 0.7);\n --muted-hint: rgba(95, 107, 122, 0.6);\n --card: #ffffff;\n --badge-bg: rgba(17, 24, 39, 0.04);\n --accent: #ff6b2c;\n --accent-2: #2563eb;\n --link-underline: rgba(37, 99, 235, 0.45);\n --glow-1: rgba(37, 99, 235, 0.12);\n --glow-2: rgba(255, 107, 44, 0.1);\n --button-grad-1: #ff7a43;\n --button-grad-2: #ff9a73;\n --button-shadow: rgba(255, 107, 44, 0.18);\n --button-shadow-hover: rgba(255, 107, 44, 0.22);\n --pulse: rgba(255, 107, 44, 0.6);\n --pulse-ring: rgba(255, 107, 44, 0.5);\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --ink: #e5e7eb;\n --muted: #a3aab5;\n --muted-subtle: rgba(163, 170, 181, 0.78);\n --muted-hint: rgba(163, 170, 181, 0.6);\n --card: #0f172a;\n --badge-bg: rgba(148, 163, 184, 0.16);\n --accent: #ff8a5a;\n --accent-2: #7aa2ff;\n --link-underline: rgba(122, 162, 255, 0.45);\n --glow-1: rgba(96, 165, 250, 0.14);\n --glow-2: rgba(251, 146, 60, 0.16);\n --button-grad-1: #f07b4d;\n --button-grad-2: #f39a76;\n --button-shadow: rgba(240, 123, 77, 0.16);\n --button-shadow-hover: rgba(240, 123, 77, 0.2);\n --pulse: rgba(255, 138, 90, 0.65);\n --pulse-ring: rgba(255, 138, 90, 0.5);\n }\n }\n\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n font-family: \"IBM Plex Sans\", \"Inter\", -apple-system, system-ui, sans-serif;\n color: var(--ink);\n background: transparent;\n width: 420px;\n height: 250px;\n margin: 0;\n padding: 0 !important;\n }\n\n #app {\n background: var(--card);\n position: relative;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n min-height: 100%;\n padding: 24px 24px 20px;\n gap: 14px;\n justify-content: center;\n }\n\n #app::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(240px 140px at 100% 0%, var(--glow-1), transparent 70%),\n radial-gradient(220px 140px at 0% 100%, var(--glow-2), transparent 70%);\n pointer-events: none;\n }\n\n .header {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: flex-start;\n padding-right: 96px;\n }\n\n .header-text {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .badge {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 10px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--muted);\n background: var(--badge-bg);\n border-radius: 999px;\n padding: 6px 10px;\n white-space: nowrap;\n position: absolute;\n top: 0;\n right: 0;\n }\n\n .pulse {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--accent);\n box-shadow: 0 0 0 0 var(--pulse);\n animation: pulse 1.6s ease-in-out infinite;\n }\n\n h1 {\n font-size: clamp(18px, 4vw, 22px);\n letter-spacing: -0.02em;\n line-height: 1.25;\n }\n\n p {\n margin: 0;\n color: var(--muted);\n font-size: 13px;\n line-height: 1.6;\n }\n\n .subtle {\n color: var(--muted-subtle);\n font-size: 12px;\n }\n\n a {\n color: var(--accent-2);\n text-decoration: none;\n border-bottom: 1px dashed var(--link-underline);\n }\n\n .content {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .footer {\n display: flex;\n flex-direction: column;\n gap: 10px;\n align-items: center;\n }\n\n .actions {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n justify-content: center;\n }\n\n button {\n appearance: none;\n border: none;\n border-radius: 999px;\n background: linear-gradient(135deg, var(--button-grad-1), var(--button-grad-2));\n color: white;\n padding: 8px 14px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 8px 16px var(--button-shadow);\n transition: transform 160ms ease, box-shadow 160ms ease;\n }\n\n button:hover {\n transform: translateY(-1px);\n box-shadow: 0 10px 20px var(--button-shadow-hover);\n }\n\n button:focus-visible {\n outline: 2px solid var(--accent-2);\n outline-offset: 2px;\n }\n\n .hint {\n font-size: 11px;\n color: var(--muted-hint);\n text-align: center;\n }\n\n @keyframes pulse {\n 0% { box-shadow: 0 0 0 0 var(--pulse-ring); }\n 70% { box-shadow: 0 0 0 10px rgba(255, 107, 44, 0); }\n 100% { box-shadow: 0 0 0 0 rgba(255, 107, 44, 0); }\n }\n</style>\n\n<div id=\"app\">\n <div class=\"header\">\n <span class=\"badge\"><span class=\"pulse\"></span>dev server</span>\n <div class=\"header-text\">\n <h1>CRXJS DEV MODE</h1>\n <p class=\"subtle\">Connecting to the Vite dev server\\u2026</p>\n </div>\n </div>\n\n <div class=\"content\">\n <p>\n Cannot connect to <a href=\"${VITE_URL}\">${VITE_URL}</a>.\n Make sure Vite is running, then reload the extension.\n </p>\n <p>This page will close automatically after the extension reloads.</p>\n </div>\n\n <div class=\"footer\">\n <div class=\"actions\">\n <button>Reload Extension</button>\n </div>\n <div class=\"hint\">Tip: if the URL is wrong, restart Vite so it picks the right port.</div>\n </div>\n</div>\n`;\ndocument.body.querySelector(\"button\")?.addEventListener(\"click\", () => {\n chrome.runtime.reload();\n});\nlet tries = 0;\nlet ready = false;\ndo {\n try {\n await fetch(VITE_URL);\n ready = true;\n } catch {\n const timeout = Math.min(100 * Math.pow(2, ++tries), 5e3);\n console.log(`[CRXJS] Vite Dev Server is not available on ${VITE_URL}`);\n console.log(`[CRXJS] Retrying in ${timeout}ms...`);\n await new Promise((resolve) => setTimeout(resolve, timeout));\n }\n} while (!ready);\nlocation.reload();\n";
1475
1618
 
1476
- var loadingPageHtml = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Vite Dev Mode</title>\n <script src=\"%SCRIPT%\" type=\"module\"></script>\n </head>\n <body\n style=\"font-family: Arial, sans-serif; padding: 20px; text-align: center\"\n >\n <h1>Vite Dev Mode</h1>\n </body>\n</html>\n";
1619
+ var loadingPageHtml = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>CRXJS DEV MODE</title>\n <script src=\"%SCRIPT%\" type=\"module\"></script>\n </head>\n <body>\n <p>An unknown error occurred. Failed to load the script.</p>\n </body>\n</html>\n";
1477
1620
 
1478
1621
  const { readFile } = promises;
1479
1622
  const pluginManifest = () => {
1480
1623
  let manifest;
1481
- let plugins;
1624
+ let plugins = [];
1482
1625
  let refId;
1483
1626
  let config;
1484
1627
  return [
@@ -1523,6 +1666,14 @@ const pluginManifest = () => {
1523
1666
  };
1524
1667
  }
1525
1668
  },
1669
+ // Use configResolved to get plugins for rolldown-vite (Vite 7) compatibility
1670
+ // In rolldown-vite, buildStart doesn't receive options.plugins, so we grab
1671
+ // them from the resolved config instead
1672
+ configResolved(resolvedConfig) {
1673
+ if (resolvedConfig.plugins) {
1674
+ plugins = resolvedConfig.plugins;
1675
+ }
1676
+ },
1526
1677
  buildStart(options) {
1527
1678
  if (options.plugins)
1528
1679
  plugins = options.plugins;
@@ -1618,8 +1769,33 @@ const pluginManifest = () => {
1618
1769
  }
1619
1770
  }
1620
1771
  if (config.command === "serve") {
1772
+ clearContentCssEntries();
1621
1773
  if (manifest2.content_scripts)
1622
- for (const { js = [], matches = [] } of manifest2.content_scripts)
1774
+ for (let i = 0; i < manifest2.content_scripts.length; i++) {
1775
+ const {
1776
+ js = [],
1777
+ css = [],
1778
+ matches = []
1779
+ } = manifest2.content_scripts[i];
1780
+ if (css.length > 0) {
1781
+ const cssEntry = registerContentCssEntry(i, css);
1782
+ contentScripts.set(
1783
+ cssEntry.virtualId,
1784
+ formatFileData({
1785
+ type: "loader",
1786
+ id: cssEntry.virtualId,
1787
+ matches,
1788
+ refId: hashScriptId({
1789
+ type: "loader",
1790
+ id: cssEntry.virtualId
1791
+ }),
1792
+ fileName: getFileName({
1793
+ type: "loader",
1794
+ id: cssEntry.virtualId
1795
+ })
1796
+ })
1797
+ );
1798
+ }
1623
1799
  for (const id2 of js) {
1624
1800
  contentScripts.set(
1625
1801
  prefix$1("/", id2),
@@ -1632,6 +1808,7 @@ const pluginManifest = () => {
1632
1808
  })
1633
1809
  );
1634
1810
  }
1811
+ }
1635
1812
  } else {
1636
1813
  if (manifest2.content_scripts)
1637
1814
  for (const { js = [], matches = [] } of manifest2.content_scripts)
@@ -1689,12 +1866,26 @@ const pluginManifest = () => {
1689
1866
  const manifestJs = bundle[manifestName];
1690
1867
  let manifest2 = decodeManifest.call(this, manifestJs.code);
1691
1868
  if (config.command === "serve") {
1692
- if (manifest2.content_scripts)
1693
- for (const script of manifest2.content_scripts) {
1694
- script.js = script.js?.map(
1869
+ if (manifest2.content_scripts) {
1870
+ const cssEntries = getContentCssEntries();
1871
+ const cssEntryMap = new Map(cssEntries.map((e) => [e.index, e]));
1872
+ for (let i = 0; i < manifest2.content_scripts.length; i++) {
1873
+ const script = manifest2.content_scripts[i];
1874
+ const cssEntry = cssEntryMap.get(i);
1875
+ const jsLoaders = (script.js || []).map(
1695
1876
  (id) => getFileName({ id, type: "loader" })
1696
1877
  );
1878
+ if (cssEntry) {
1879
+ const cssLoader = getFileName({
1880
+ id: cssEntry.virtualId,
1881
+ type: "loader"
1882
+ });
1883
+ script.js = [cssLoader, ...jsLoaders];
1884
+ } else {
1885
+ script.js = jsLoaders;
1886
+ }
1697
1887
  }
1888
+ }
1698
1889
  } else {
1699
1890
  if (manifest2.background && "service_worker" in manifest2.background) {
1700
1891
  const ref = manifest2.background.service_worker;
@@ -1743,6 +1934,7 @@ const pluginManifest = () => {
1743
1934
  }
1744
1935
  }
1745
1936
  const assetTypes = [
1937
+ "contentStyles",
1746
1938
  "icons",
1747
1939
  "locales",
1748
1940
  "rulesets",
@@ -1780,10 +1972,7 @@ Public dir: "${config.publicDir}"`
1780
1972
  const refId2 = this.emitFile({
1781
1973
  type: "asset",
1782
1974
  name: "loading-page.js",
1783
- source: loadingPageScript.replace(
1784
- "%PORT%",
1785
- `${config.server.port ?? 0}`
1786
- )
1975
+ source: loadingPageScript.replace("%PROTO%", config.server.https ? "https" : "http").replace("%PORT%", `${config.server.port ?? 0}`)
1787
1976
  });
1788
1977
  const loadingPageScriptName = this.getFileName(refId2);
1789
1978
  files.html.map(
@@ -1875,11 +2064,12 @@ const defineDynamicResource = ({
1875
2064
  });
1876
2065
  const DYNAMIC_RESOURCE = "<dynamic_resource>";
1877
2066
 
1878
- _debug("web-acc-res");
2067
+ const debug = _debug("web-acc-res");
1879
2068
  const pluginWebAccessibleResources = () => {
1880
2069
  let config;
1881
2070
  let injectCss;
1882
2071
  let browser;
2072
+ let userWantsViteManifest;
1883
2073
  return [
1884
2074
  {
1885
2075
  name: "crx:web-accessible-resources",
@@ -1919,6 +2109,7 @@ const pluginWebAccessibleResources = () => {
1919
2109
  const contentScripts2 = opts.contentScripts || {};
1920
2110
  browser = opts.browser || "chrome";
1921
2111
  injectCss = contentScripts2.injectCss ?? true;
2112
+ userWantsViteManifest = build?.manifest;
1922
2113
  return { ...config2, build: { ...build, manifest: command === "build" } };
1923
2114
  },
1924
2115
  configResolved(_config) {
@@ -2053,6 +2244,18 @@ const pluginWebAccessibleResources = () => {
2053
2244
  delete manifest.web_accessible_resources;
2054
2245
  else
2055
2246
  manifest.web_accessible_resources = combinedResources;
2247
+ if (!userWantsViteManifest) {
2248
+ const viteMajorVersion = parseInt(version.split(".")[0]);
2249
+ const manifestPath = viteMajorVersion > 4 ? ".vite/manifest.json" : "manifest.json";
2250
+ if (bundle[manifestPath]) {
2251
+ debug(
2252
+ "Removing Vite manifest: %s (userWantsViteManifest=%s)",
2253
+ manifestPath,
2254
+ userWantsViteManifest
2255
+ );
2256
+ delete bundle[manifestPath];
2257
+ }
2258
+ }
2056
2259
  return manifest;
2057
2260
  }
2058
2261
  }
package/index.d.cts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/index.d.ts'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crxjs/vite-plugin",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Build Chrome Extensions with this Vite plugin.",
5
5
  "keywords": [
6
6
  "rollup-plugin",
@@ -27,9 +27,14 @@
27
27
  "type": "module",
28
28
  "exports": {
29
29
  ".": {
30
- "require": "./index.cjs",
31
- "import": "./dist/index.mjs",
32
- "types": "./dist/index.d.ts"
30
+ "import": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.mjs"
33
+ },
34
+ "require": {
35
+ "types": "./index.d.cts",
36
+ "default": "./index.cjs"
37
+ }
33
38
  },
34
39
  "./*": "./*",
35
40
  "./client": {
@@ -44,13 +49,13 @@
44
49
  "types",
45
50
  "manifest.schema.json",
46
51
  "index.cjs",
52
+ "index.d.cts",
47
53
  "client.d.ts"
48
54
  ],
49
55
  "dependencies": {
50
56
  "@rollup/pluginutils": "^4.1.2",
51
57
  "@webcomponents/custom-elements": "^1.5.0",
52
58
  "acorn-walk": "^8.2.0",
53
- "cheerio": "^1.0.0-rc.10",
54
59
  "convert-source-map": "^1.7.0",
55
60
  "debug": "^4.3.3",
56
61
  "es-module-lexer": "^0.10.0",
@@ -58,6 +63,7 @@
58
63
  "fs-extra": "^10.0.1",
59
64
  "jsesc": "^3.0.2",
60
65
  "magic-string": "^0.30.12",
66
+ "node-html-parser": "^7.0.2",
61
67
  "pathe": "^2.0.1",
62
68
  "picocolors": "^1.1.1",
63
69
  "react-refresh": "^0.13.0",
@@ -102,12 +108,17 @@
102
108
  "svelte": "^3.48.0",
103
109
  "type-fest": "^5.1.0",
104
110
  "typescript": "^4.6.4",
111
+ "unocss": "^66.5.12",
105
112
  "vite": "^3.2.11",
106
113
  "vite-plugin-inspect": "0.7.25",
107
114
  "vitest": "0.28.5",
108
115
  "vue": "3.2.47"
109
116
  },
117
+ "peerDependencies": {
118
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
119
+ },
110
120
  "scripts": {
121
+ "release": "bumpp",
111
122
  "format": "prettier -w -c ../../.prettierrc.yaml",
112
123
  "build": "run-s build:clean build:js",
113
124
  "build:clean": "rimraf dist",