@crxjs/vite-plugin 2.6.1 → 2.7.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.
Files changed (2) hide show
  1. package/dist/index.mjs +123 -24
  2. package/package.json +7 -9
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { simple } from 'acorn-walk';
2
- import { createHash } from 'crypto';
2
+ import { createHash, randomBytes } from 'crypto';
3
3
  import debug$5 from 'debug';
4
4
  import { join, normalize, dirname, basename, isAbsolute, relative, resolve, parse as parse$1 } from 'pathe';
5
5
  import { Subject, filter, ReplaySubject, switchMap, of, startWith, tap, debounceTime, map, share, BehaviorSubject, mergeMap, firstValueFrom, takeUntil, first, toArray, retry, concatWith, Subscription, buffer } from 'rxjs';
@@ -58,7 +58,7 @@ 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 = __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 if (!__LIVE_RELOAD__) {\n if (payload.event === \"crx:runtime-reload\") {\n console.log(\"[crx] runtime reload suppressed (liveReload disabled)\");\n }\n return;\n }\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 if (__LIVE_RELOAD__) {\n handleCrxHmrPayload({\n type: \"custom\",\n event: \"crx:runtime-reload\"\n });\n } else {\n console.log(\n \"[crx] server reconnected, skipping reload (liveReload disabled)\"\n );\n }\n});\n";
61
+ var workerHmrClient = "const crxClientPortName = `@crx/client:${__CRX_HMR_TOKEN__}`;\nconst 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();\nfunction isExternalSenderAllowed(port) {\n return !port.sender?.id || port.sender.id === chrome.runtime.id;\n}\nfunction handlePort(port, { external = false } = {}) {\n if (port.name === crxClientPortName && (!external || isExternalSenderAllowed(port))) {\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}\nchrome.runtime.onConnect.addListener(handlePort);\nchrome.runtime.onConnectExternal.addListener(\n (port) => handlePort(port, { external: true })\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 if (!__LIVE_RELOAD__) {\n if (payload.event === \"crx:runtime-reload\") {\n console.log(\"[crx] runtime reload suppressed (liveReload disabled)\");\n }\n return;\n }\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 if (__LIVE_RELOAD__) {\n handleCrxHmrPayload({\n type: \"custom\",\n event: \"crx:runtime-reload\"\n });\n } else {\n console.log(\n \"[crx] server reconnected, skipping reload (liveReload disabled)\"\n );\n }\n});\n";
62
62
 
63
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);
@@ -274,6 +274,18 @@ async function fileReady(script) {
274
274
  await Promise.all(deps.map(fileReady));
275
275
  }
276
276
 
277
+ const crxHmrTokens = /* @__PURE__ */ new WeakMap();
278
+ function getCrxHmrToken(config) {
279
+ if (config.webSocketToken)
280
+ return config.webSocketToken;
281
+ let token = crxHmrTokens.get(config);
282
+ if (!token) {
283
+ token = randomBytes(16).toString("hex");
284
+ crxHmrTokens.set(config, token);
285
+ }
286
+ return token;
287
+ }
288
+
277
289
  const viteClientId = "/@vite/client";
278
290
  const customElementsId = "/@webcomponents/custom-elements";
279
291
  const contentHmrPortId = "/@crx/client-port";
@@ -312,7 +324,12 @@ const pluginBackground = () => {
312
324
  if (id === workerClientId) {
313
325
  const base = `${config.server.https ? "https" : "http"}://localhost:${config.server.port}/`;
314
326
  return defineClientValues(
315
- workerHmrClient.replace("__BASE__", JSON.stringify(base)).replace("__LIVE_RELOAD__", JSON.stringify(liveReload)),
327
+ workerHmrClient.replace("__BASE__", JSON.stringify(base)).replace("__LIVE_RELOAD__", JSON.stringify(liveReload)).replace(
328
+ "__CRX_HMR_TOKEN__",
329
+ JSON.stringify(
330
+ getCrxHmrToken(config)
331
+ )
332
+ ),
316
333
  config
317
334
  );
318
335
  }
@@ -430,11 +447,11 @@ function pluginExtensionCors() {
430
447
  };
431
448
  }
432
449
 
433
- var contentHmrPort = "function isCrxHMRPayload(x) {\n return x.type === \"custom\" && x.event.startsWith(\"crx:\");\n}\nclass HMRPort {\n port;\n callbacks = /* @__PURE__ */ new Map();\n constructor() {\n setInterval(() => {\n try {\n this.port?.postMessage({ data: \"ping\" });\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"Extension context invalidated.\")) {\n location.reload();\n } else\n throw error;\n }\n }, __CRX_HMR_TIMEOUT__);\n setInterval(this.initPort, 5 * 60 * 1e3);\n this.initPort();\n }\n initPort = () => {\n this.port?.disconnect();\n this.port = chrome.runtime.connect({ name: \"@crx/client\" });\n this.port.onDisconnect.addListener(this.handleDisconnect.bind(this));\n this.port.onMessage.addListener(this.handleMessage.bind(this));\n this.port.postMessage({ type: \"connected\" });\n };\n handleDisconnect = () => {\n if (this.callbacks.has(\"close\"))\n for (const cb of this.callbacks.get(\"close\")) {\n cb({ wasClean: true });\n }\n };\n handleMessage = (message) => {\n const forward = (data) => {\n if (this.callbacks.has(\"message\"))\n for (const cb of this.callbacks.get(\"message\")) {\n cb({ data });\n }\n };\n const payload = JSON.parse(message.data);\n if (isCrxHMRPayload(payload)) {\n if (payload.event === \"crx:runtime-reload\") {\n if (__CRX_LIVE_RELOAD__) {\n console.log(\"[crx] runtime reload\");\n setTimeout(() => location.reload(), 500);\n } else {\n console.log(\"[crx] runtime reload suppressed (liveReload disabled)\");\n }\n } else {\n forward(JSON.stringify(payload.data));\n }\n } else {\n forward(message.data);\n }\n };\n addEventListener = (event, callback) => {\n const cbs = this.callbacks.get(event) ?? /* @__PURE__ */ new Set();\n cbs.add(callback);\n this.callbacks.set(event, cbs);\n };\n send = (data) => {\n if (this.port)\n this.port.postMessage({ data });\n else\n throw new Error(\"HMRPort is not initialized\");\n };\n}\n\nexport { HMRPort };\n";
450
+ var contentHmrPort = "const crxClientPortName = `@crx/client:${__CRX_HMR_TOKEN__}`;\nfunction hasOwnExtensionRuntime(runtime2, extensionId2) {\n try {\n return new URL(runtime2.getURL(\"\")).host === extensionId2;\n } catch {\n return false;\n }\n}\nconst runtime = typeof chrome === \"undefined\" ? void 0 : chrome.runtime;\nconst extensionId = new URL(import.meta.url).host;\nconst connectsToOwnRuntime = runtime ? hasOwnExtensionRuntime(runtime, extensionId) : false;\nfunction isCrxHMRPayload(x) {\n return x.type === \"custom\" && x.event.startsWith(\"crx:\");\n}\nclass HMRPort {\n port;\n callbacks = /* @__PURE__ */ new Map();\n constructor() {\n setInterval(() => {\n try {\n this.port?.postMessage({ data: \"ping\" });\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"Extension context invalidated.\")) {\n location.reload();\n } else\n throw error;\n }\n }, __CRX_HMR_TIMEOUT__);\n setInterval(this.initPort, 5 * 60 * 1e3);\n this.initPort();\n }\n initPort = () => {\n if (!runtime)\n throw new Error(\"[crx] chrome.runtime is not available\");\n const connectInfo = { name: crxClientPortName };\n this.port?.disconnect();\n this.port = connectsToOwnRuntime ? runtime.connect(connectInfo) : runtime.connect(extensionId, connectInfo);\n this.port.onDisconnect.addListener(this.handleDisconnect.bind(this));\n this.port.onMessage.addListener(this.handleMessage.bind(this));\n this.port.postMessage({ type: \"connected\" });\n };\n handleDisconnect = () => {\n if (this.callbacks.has(\"close\"))\n for (const cb of this.callbacks.get(\"close\")) {\n cb({ wasClean: true });\n }\n };\n handleMessage = (message) => {\n const forward = (data) => {\n if (this.callbacks.has(\"message\"))\n for (const cb of this.callbacks.get(\"message\")) {\n cb({ data });\n }\n };\n const payload = JSON.parse(message.data);\n if (isCrxHMRPayload(payload)) {\n if (payload.event === \"crx:runtime-reload\") {\n if (__CRX_LIVE_RELOAD__) {\n console.log(\"[crx] runtime reload\");\n setTimeout(() => location.reload(), 500);\n } else {\n console.log(\"[crx] runtime reload suppressed (liveReload disabled)\");\n }\n } else {\n forward(JSON.stringify(payload.data));\n }\n } else {\n forward(message.data);\n }\n };\n addEventListener = (event, callback) => {\n const cbs = this.callbacks.get(event) ?? /* @__PURE__ */ new Set();\n cbs.add(callback);\n this.callbacks.set(event, cbs);\n };\n send = (data) => {\n if (this.port)\n this.port.postMessage({ data });\n else\n throw new Error(\"HMRPort is not initialized\");\n };\n}\n\nexport { HMRPort };\n";
434
451
 
435
452
  var contentDevLoader = "(function () {\n 'use strict';\n\n const injectTime = performance.now();\n (async () => {\n if (__PREAMBLE__)\n await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__PREAMBLE__)\n );\n await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__CLIENT__)\n );\n const { onExecute } = await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__SCRIPT__)\n );\n onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });\n })().catch(console.error);\n\n})();\n";
436
453
 
437
- var contentDevMainLoader = "(function () {\n 'use strict';\n\n const injectTime = performance.now();\n (async () => {\n console.warn(__SCRIPT__, \"Content-script doesn't support HMR because the world is MAIN\");\n const { onExecute } = await import(\n /* @vite-ignore */\n __SCRIPT__\n );\n onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });\n })().catch(console.error);\n\n})();\n";
454
+ var contentDevMainLoader = "(function () {\n 'use strict';\n\n const injectTime = performance.now();\n (async () => {\n try {\n if (__PREAMBLE__)\n await import(\n /* @vite-ignore */\n __PREAMBLE__\n );\n await import(\n /* @vite-ignore */\n __CLIENT__\n );\n } catch (error) {\n console.warn(\"[crx] MAIN world HMR client failed to load\", error);\n }\n const { onExecute } = await import(\n /* @vite-ignore */\n __SCRIPT__\n );\n onExecute?.({\n perf: { injectTime, loadTime: performance.now() - injectTime }\n });\n })().catch(console.error);\n\n})();\n";
438
455
 
439
456
  var contentProLoader = "(function () {\n 'use strict';\n\n const injectTime = performance.now();\n (async () => {\n const { onExecute } = await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__SCRIPT__)\n );\n onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });\n })().catch(console.error);\n\n})();\n";
440
457
 
@@ -474,11 +491,15 @@ function createProLoader({ fileName }) {
474
491
  return contentProLoader.replace(/__SCRIPT__/g, JSON.stringify(fileName));
475
492
  }
476
493
  function createDevMainLoader({
494
+ preamble,
495
+ client,
477
496
  fileName
478
497
  }) {
479
- return contentDevMainLoader.replace(/__SCRIPT__/g, JSON.stringify(fileName)).replace(/__TIMESTAMP__/g, JSON.stringify(Date.now()));
498
+ return contentDevMainLoader.replace(/__PREAMBLE__/g, JSON.stringify(preamble)).replace(/__CLIENT__/g, JSON.stringify(client)).replace(/__SCRIPT__/g, JSON.stringify(fileName)).replace(/__TIMESTAMP__/g, JSON.stringify(Date.now()));
480
499
  }
481
- function createProMainLoader({ fileName }) {
500
+ function createProMainLoader({
501
+ fileName
502
+ }) {
482
503
  return contentProMainLoader.replace(/__SCRIPT__/g, JSON.stringify(fileName));
483
504
  }
484
505
 
@@ -657,14 +678,16 @@ ${sourceMap}
657
678
  mergeMap(async ({ target, code, deps }) => {
658
679
  await lexer.init;
659
680
  const [imports] = lexer.parse(code, fileName);
660
- const depSet = new Set(deps);
681
+ const isSelfDependency = (id) => getFileName({ type: "module", id }) === fileName;
682
+ const depSet = new Set(deps.filter((id) => !isSelfDependency(id)));
661
683
  const magic = new MagicString(code);
662
684
  for (const i of imports)
663
685
  if (i.n) {
664
- depSet.add(i.n);
665
- const fileName2 = getFileName({ type: "module", id: i.n });
686
+ const depFileName = getFileName({ type: "module", id: i.n });
687
+ if (!isSelfDependency(i.n))
688
+ depSet.add(i.n);
666
689
  const fullImport = code.substring(i.s, i.e);
667
- magic.overwrite(i.s, i.e, fullImport.replace(i.n, `/${fileName2}`));
690
+ magic.overwrite(i.s, i.e, fullImport.replace(i.n, `/${depFileName}`));
668
691
  }
669
692
  return { target, source: magic.toString(), deps: [...depSet] };
670
693
  })
@@ -895,6 +918,37 @@ async function write(fileId) {
895
918
  return { start: start2, close: close2, deps };
896
919
  }
897
920
 
921
+ function asRelativeImport(fromFileName, toFileName) {
922
+ const path = relative(dirname(fromFileName), toFileName);
923
+ return path.startsWith(".") ? path : `./${path}`;
924
+ }
925
+ function getExternallyConnectableMatch(match) {
926
+ if (match === "<all_urls>")
927
+ return null;
928
+ const parsed = /^(\*|https?):\/\/([^/]+)\/.*$/.exec(match);
929
+ if (!parsed)
930
+ return null;
931
+ const [, , host] = parsed;
932
+ if (host === "*")
933
+ return null;
934
+ return match;
935
+ }
936
+ function getExternallyConnectableMatches(matches) {
937
+ const result = /* @__PURE__ */ new Set();
938
+ const unsupported = /* @__PURE__ */ new Set();
939
+ for (const match of matches) {
940
+ const externallyConnectableMatch = getExternallyConnectableMatch(match);
941
+ if (externallyConnectableMatch) {
942
+ result.add(externallyConnectableMatch);
943
+ } else {
944
+ unsupported.add(match);
945
+ }
946
+ }
947
+ return {
948
+ matches: [...result],
949
+ unsupported: [...unsupported]
950
+ };
951
+ }
898
952
  const pluginContentScripts = () => {
899
953
  const pluginName = "crx:content-scripts";
900
954
  let server;
@@ -903,6 +957,8 @@ const pluginContentScripts = () => {
903
957
  let liveReload = true;
904
958
  let sub = new Subscription();
905
959
  const worldMainIds = /* @__PURE__ */ new Set();
960
+ const worldMainExternallyConnectableMatches = /* @__PURE__ */ new Set();
961
+ const unsupportedWorldMainExternallyConnectableMatches = /* @__PURE__ */ new Set();
906
962
  const findWorldMainIds = async (config, env) => {
907
963
  const { manifest: _manifest } = await getOptions(config);
908
964
  const manifest = await (typeof _manifest === "function" ? _manifest(env) : _manifest);
@@ -911,16 +967,32 @@ const pluginContentScripts = () => {
911
967
  js.forEach((path) => worldMainIds.add(prefix$1("/", path)));
912
968
  }
913
969
  });
914
- if (worldMainIds.size) {
915
- const name = `[${pluginName}]`;
916
- const message = pc.yellow(
917
- [
918
- `${name} Some content-scripts don't support HMR because the world is MAIN:`,
919
- ...[...worldMainIds].map((id) => ` ${id}`)
920
- ].join("\r\n")
921
- );
922
- console.log(message);
923
- }
970
+ (manifest.content_scripts || []).forEach(({ world, matches = [] }) => {
971
+ if (world === "MAIN") {
972
+ const externallyConnectable = getExternallyConnectableMatches(matches);
973
+ externallyConnectable.matches.forEach(
974
+ (match) => worldMainExternallyConnectableMatches.add(match)
975
+ );
976
+ externallyConnectable.unsupported.forEach(
977
+ (match) => unsupportedWorldMainExternallyConnectableMatches.add(match)
978
+ );
979
+ }
980
+ });
981
+ };
982
+ const warnUnsupportedWorldMainExternallyConnectableMatches = () => {
983
+ if (unsupportedWorldMainExternallyConnectableMatches.size === 0)
984
+ return;
985
+ const name = `[${pluginName}]`;
986
+ const message = pc.yellow(
987
+ [
988
+ `${name} MAIN world HMR requires externally_connectable.matches. CRX cannot auto-add these Chrome-rejected content-script match patterns:`,
989
+ ...[...unsupportedWorldMainExternallyConnectableMatches].map(
990
+ (match) => ` ${match}`
991
+ ),
992
+ "Add explicit http(s) host addresses to your content script matches during development if you want MAIN world HMR for those pages."
993
+ ].join("\r\n")
994
+ );
995
+ console.warn(message);
924
996
  };
925
997
  return [
926
998
  {
@@ -928,6 +1000,7 @@ const pluginContentScripts = () => {
928
1000
  apply: "serve",
929
1001
  async config(config, env) {
930
1002
  await findWorldMainIds(config, env);
1003
+ warnUnsupportedWorldMainExternallyConnectableMatches();
931
1004
  const opts = await getOptions(config);
932
1005
  const { contentScripts: contentScripts2 = {} } = opts;
933
1006
  hmrTimeout = contentScripts2.hmrTimeout ?? 5e3;
@@ -955,11 +1028,20 @@ const pluginContentScripts = () => {
955
1028
  preamble = add({ type: "module", id: preambleId });
956
1029
  const client = add({ type: "module", id: viteClientId });
957
1030
  const file = add({ type: "module", id });
1031
+ const loaderFileName = getFileName({ type: "loader", id });
958
1032
  const loader = add({
959
1033
  type: "asset",
960
- id: getFileName({ type: "loader", id }),
1034
+ id: loaderFileName,
961
1035
  source: worldMainIds.has(file.id) ? createDevMainLoader({
962
- fileName: `./${file.fileName.split("/").at(-1)}`
1036
+ preamble: preamble.fileName ? asRelativeImport(loaderFileName, preamble.fileName) : "",
1037
+ client: asRelativeImport(
1038
+ loaderFileName,
1039
+ client.fileName
1040
+ ),
1041
+ fileName: asRelativeImport(
1042
+ loaderFileName,
1043
+ file.fileName
1044
+ )
963
1045
  }) : createDevLoader({
964
1046
  preamble: preamble.fileName,
965
1047
  client: client.fileName,
@@ -989,13 +1071,30 @@ const pluginContentScripts = () => {
989
1071
  return defined;
990
1072
  }
991
1073
  if (id === contentHmrPortId) {
992
- const defined = contentHmrPort.replace("__CRX_HMR_TIMEOUT__", JSON.stringify(hmrTimeout)).replace("__CRX_LIVE_RELOAD__", JSON.stringify(liveReload));
1074
+ const defined = contentHmrPort.replace("__CRX_HMR_TIMEOUT__", JSON.stringify(hmrTimeout)).replace("__CRX_LIVE_RELOAD__", JSON.stringify(liveReload)).replace(
1075
+ "__CRX_HMR_TOKEN__",
1076
+ JSON.stringify(
1077
+ getCrxHmrToken(server.config)
1078
+ )
1079
+ );
993
1080
  return defined;
994
1081
  }
995
1082
  },
996
1083
  closeBundle() {
997
1084
  sub.unsubscribe();
998
1085
  sub = new Subscription();
1086
+ },
1087
+ transformCrxManifest(manifest) {
1088
+ if (worldMainExternallyConnectableMatches.size === 0)
1089
+ return null;
1090
+ manifest.externally_connectable = manifest.externally_connectable ?? {};
1091
+ manifest.externally_connectable.matches = [
1092
+ .../* @__PURE__ */ new Set([
1093
+ ...manifest.externally_connectable.matches ?? [],
1094
+ ...worldMainExternallyConnectableMatches
1095
+ ])
1096
+ ];
1097
+ return manifest;
999
1098
  }
1000
1099
  },
1001
1100
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crxjs/vite-plugin",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Build Chrome Extensions with this Vite plugin.",
5
5
  "keywords": [
6
6
  "rollup-plugin",
@@ -54,28 +54,26 @@
54
54
  ],
55
55
  "dependencies": {
56
56
  "@webcomponents/custom-elements": "^1.5.0",
57
- "acorn-walk": "^8.2.0",
57
+ "acorn-walk": "^8.3.5",
58
58
  "convert-source-map": "^1.7.0",
59
59
  "debug": "^4.3.3",
60
60
  "es-module-lexer": "^0.10.0",
61
- "tinyglobby": "^0.2.15",
61
+ "tinyglobby": "^0.2.17",
62
62
  "fs-extra": "^10.0.1",
63
63
  "jsesc": "^3.0.2",
64
64
  "magic-string": "^0.30.12",
65
- "node-html-parser": "^7.0.2",
65
+ "node-html-parser": "^7.1.0",
66
66
  "pathe": "^2.0.1",
67
67
  "picocolors": "^1.1.1",
68
- "react-refresh": "^0.13.0",
69
68
  "rollup": "2.80.0",
70
69
  "rxjs": "7.5.7"
71
70
  },
72
71
  "devDependencies": {
73
72
  "@arethetypeswrong/cli": "0.18.3",
74
- "@eslint/eslintrc": "3.3.3",
73
+ "@eslint/eslintrc": "3.3.5",
75
74
  "@eslint/js": "9.26.0",
76
75
  "@extend-chrome/messages": "1.2.2",
77
76
  "@extend-chrome/storage": "1.5.0",
78
- "@rollup/pluginutils": "^4.1.2",
79
77
  "@rollup/plugin-alias": "4.0.4",
80
78
  "@rollup/plugin-commonjs": "21.1.0",
81
79
  "@rollup/plugin-json": "^5.0.0",
@@ -84,10 +82,10 @@
84
82
  "@types/acorn": "4.0.6",
85
83
  "@types/chrome": "0.0.237",
86
84
  "@types/convert-source-map": "^2.0.0",
87
- "@types/debug": "4.1.7",
85
+ "@types/debug": "4.1.13",
88
86
  "@types/fs-extra": "9.0.13",
89
87
  "@types/jest-image-snapshot": "^5.1.0",
90
- "@types/jsesc": "3.0.1",
88
+ "@types/jsesc": "3.0.3",
91
89
  "@types/node": "17.0.18",
92
90
  "@types/react": "17.0.52",
93
91
  "@types/react-dom": "17.0.18",