@crxjs/vite-plugin 2.6.1 → 2.7.1

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 +140 -32
  2. package/package.json +11 -13
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
  })
@@ -775,6 +798,19 @@ async function allFilesReady() {
775
798
 
776
799
  const { outputFile } = fsx;
777
800
  const debug$4 = _debug("file-writer");
801
+ function getRollupInputOptions(options) {
802
+ const {
803
+ platform: _platform,
804
+ resolve: _resolve,
805
+ transform: _transform,
806
+ moduleTypes: _moduleTypes,
807
+ optimization: _optimization,
808
+ experimental: _experimental,
809
+ cwd: _cwd,
810
+ ...rollupOptions
811
+ } = options;
812
+ return rollupOptions;
813
+ }
778
814
  function queueWrite(script, previous) {
779
815
  if (!previous)
780
816
  return write(script);
@@ -788,9 +824,10 @@ async function start({
788
824
  (p) => p.name?.startsWith("crx:")
789
825
  );
790
826
  const { rollupOptions, outDir } = server.config.build;
827
+ const rollupInputOptions = getRollupInputOptions(rollupOptions);
791
828
  const inputOptions = {
792
829
  input: "index.html",
793
- ...rollupOptions,
830
+ ...rollupInputOptions,
794
831
  plugins
795
832
  };
796
833
  const rollupOutputOptions = [rollupOptions.output].flat()[0];
@@ -895,6 +932,37 @@ async function write(fileId) {
895
932
  return { start: start2, close: close2, deps };
896
933
  }
897
934
 
935
+ function asRelativeImport(fromFileName, toFileName) {
936
+ const path = relative(dirname(fromFileName), toFileName);
937
+ return path.startsWith(".") ? path : `./${path}`;
938
+ }
939
+ function getExternallyConnectableMatch(match) {
940
+ if (match === "<all_urls>")
941
+ return null;
942
+ const parsed = /^(\*|https?):\/\/([^/]+)\/.*$/.exec(match);
943
+ if (!parsed)
944
+ return null;
945
+ const [, , host] = parsed;
946
+ if (host === "*")
947
+ return null;
948
+ return match;
949
+ }
950
+ function getExternallyConnectableMatches(matches) {
951
+ const result = /* @__PURE__ */ new Set();
952
+ const unsupported = /* @__PURE__ */ new Set();
953
+ for (const match of matches) {
954
+ const externallyConnectableMatch = getExternallyConnectableMatch(match);
955
+ if (externallyConnectableMatch) {
956
+ result.add(externallyConnectableMatch);
957
+ } else {
958
+ unsupported.add(match);
959
+ }
960
+ }
961
+ return {
962
+ matches: [...result],
963
+ unsupported: [...unsupported]
964
+ };
965
+ }
898
966
  const pluginContentScripts = () => {
899
967
  const pluginName = "crx:content-scripts";
900
968
  let server;
@@ -903,6 +971,8 @@ const pluginContentScripts = () => {
903
971
  let liveReload = true;
904
972
  let sub = new Subscription();
905
973
  const worldMainIds = /* @__PURE__ */ new Set();
974
+ const worldMainExternallyConnectableMatches = /* @__PURE__ */ new Set();
975
+ const unsupportedWorldMainExternallyConnectableMatches = /* @__PURE__ */ new Set();
906
976
  const findWorldMainIds = async (config, env) => {
907
977
  const { manifest: _manifest } = await getOptions(config);
908
978
  const manifest = await (typeof _manifest === "function" ? _manifest(env) : _manifest);
@@ -911,16 +981,32 @@ const pluginContentScripts = () => {
911
981
  js.forEach((path) => worldMainIds.add(prefix$1("/", path)));
912
982
  }
913
983
  });
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
- }
984
+ (manifest.content_scripts || []).forEach(({ world, matches = [] }) => {
985
+ if (world === "MAIN") {
986
+ const externallyConnectable = getExternallyConnectableMatches(matches);
987
+ externallyConnectable.matches.forEach(
988
+ (match) => worldMainExternallyConnectableMatches.add(match)
989
+ );
990
+ externallyConnectable.unsupported.forEach(
991
+ (match) => unsupportedWorldMainExternallyConnectableMatches.add(match)
992
+ );
993
+ }
994
+ });
995
+ };
996
+ const warnUnsupportedWorldMainExternallyConnectableMatches = () => {
997
+ if (unsupportedWorldMainExternallyConnectableMatches.size === 0)
998
+ return;
999
+ const name = `[${pluginName}]`;
1000
+ const message = pc.yellow(
1001
+ [
1002
+ `${name} MAIN world HMR requires externally_connectable.matches. CRX cannot auto-add these Chrome-rejected content-script match patterns:`,
1003
+ ...[...unsupportedWorldMainExternallyConnectableMatches].map(
1004
+ (match) => ` ${match}`
1005
+ ),
1006
+ "Add explicit http(s) host addresses to your content script matches during development if you want MAIN world HMR for those pages."
1007
+ ].join("\r\n")
1008
+ );
1009
+ console.warn(message);
924
1010
  };
925
1011
  return [
926
1012
  {
@@ -928,6 +1014,7 @@ const pluginContentScripts = () => {
928
1014
  apply: "serve",
929
1015
  async config(config, env) {
930
1016
  await findWorldMainIds(config, env);
1017
+ warnUnsupportedWorldMainExternallyConnectableMatches();
931
1018
  const opts = await getOptions(config);
932
1019
  const { contentScripts: contentScripts2 = {} } = opts;
933
1020
  hmrTimeout = contentScripts2.hmrTimeout ?? 5e3;
@@ -955,11 +1042,20 @@ const pluginContentScripts = () => {
955
1042
  preamble = add({ type: "module", id: preambleId });
956
1043
  const client = add({ type: "module", id: viteClientId });
957
1044
  const file = add({ type: "module", id });
1045
+ const loaderFileName = getFileName({ type: "loader", id });
958
1046
  const loader = add({
959
1047
  type: "asset",
960
- id: getFileName({ type: "loader", id }),
1048
+ id: loaderFileName,
961
1049
  source: worldMainIds.has(file.id) ? createDevMainLoader({
962
- fileName: `./${file.fileName.split("/").at(-1)}`
1050
+ preamble: preamble.fileName ? asRelativeImport(loaderFileName, preamble.fileName) : "",
1051
+ client: asRelativeImport(
1052
+ loaderFileName,
1053
+ client.fileName
1054
+ ),
1055
+ fileName: asRelativeImport(
1056
+ loaderFileName,
1057
+ file.fileName
1058
+ )
963
1059
  }) : createDevLoader({
964
1060
  preamble: preamble.fileName,
965
1061
  client: client.fileName,
@@ -989,13 +1085,30 @@ const pluginContentScripts = () => {
989
1085
  return defined;
990
1086
  }
991
1087
  if (id === contentHmrPortId) {
992
- const defined = contentHmrPort.replace("__CRX_HMR_TIMEOUT__", JSON.stringify(hmrTimeout)).replace("__CRX_LIVE_RELOAD__", JSON.stringify(liveReload));
1088
+ const defined = contentHmrPort.replace("__CRX_HMR_TIMEOUT__", JSON.stringify(hmrTimeout)).replace("__CRX_LIVE_RELOAD__", JSON.stringify(liveReload)).replace(
1089
+ "__CRX_HMR_TOKEN__",
1090
+ JSON.stringify(
1091
+ getCrxHmrToken(server.config)
1092
+ )
1093
+ );
993
1094
  return defined;
994
1095
  }
995
1096
  },
996
1097
  closeBundle() {
997
1098
  sub.unsubscribe();
998
1099
  sub = new Subscription();
1100
+ },
1101
+ transformCrxManifest(manifest) {
1102
+ if (worldMainExternallyConnectableMatches.size === 0)
1103
+ return null;
1104
+ manifest.externally_connectable = manifest.externally_connectable ?? {};
1105
+ manifest.externally_connectable.matches = [
1106
+ .../* @__PURE__ */ new Set([
1107
+ ...manifest.externally_connectable.matches ?? [],
1108
+ ...worldMainExternallyConnectableMatches
1109
+ ])
1110
+ ];
1111
+ return manifest;
999
1112
  }
1000
1113
  },
1001
1114
  {
@@ -1005,11 +1118,8 @@ const pluginContentScripts = () => {
1005
1118
  async config(config, env) {
1006
1119
  await findWorldMainIds(config, env);
1007
1120
  return {
1008
- ...config,
1009
1121
  build: {
1010
- ...config.build,
1011
1122
  rollupOptions: {
1012
- ...config.build?.rollupOptions,
1013
1123
  // keep exports for content script module api
1014
1124
  preserveEntrySignatures: config.build?.rollupOptions?.preserveEntrySignatures ?? "exports-only"
1015
1125
  }
@@ -1875,7 +1985,7 @@ const pluginHMR = () => {
1875
1985
  server.hmr = {};
1876
1986
  server.hmr = server.hmr ?? {};
1877
1987
  server.hmr.host = "localhost";
1878
- return { server, ...config2 };
1988
+ return { server };
1879
1989
  },
1880
1990
  // server should ignore outdir
1881
1991
  configResolved(_config) {
@@ -2263,9 +2373,7 @@ const pluginManifest = () => {
2263
2373
  for (const x of [js, sw, html].flat())
2264
2374
  set.add(x);
2265
2375
  return {
2266
- ...config2,
2267
2376
  optimizeDeps: {
2268
- ...config2.optimizeDeps,
2269
2377
  entries: [...set]
2270
2378
  }
2271
2379
  };
@@ -2753,7 +2861,7 @@ const pluginWebAccessibleResources = () => {
2753
2861
  browser = opts.browser || "chrome";
2754
2862
  injectCss = contentScripts2.injectCss ?? true;
2755
2863
  userWantsViteManifest = build?.manifest;
2756
- return { ...config2, build: { ...build, manifest: command === "build" } };
2864
+ return { build: { manifest: command === "build" } };
2757
2865
  },
2758
2866
  configResolved(_config) {
2759
2867
  config = _config;
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.1",
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",
58
- "convert-source-map": "^1.7.0",
59
- "debug": "^4.3.3",
57
+ "acorn-walk": "^8.3.5",
58
+ "convert-source-map": "^2.0.0",
59
+ "debug": "^4.4.3",
60
60
  "es-module-lexer": "^0.10.0",
61
- "tinyglobby": "^0.2.15",
62
- "fs-extra": "^10.0.1",
61
+ "tinyglobby": "^0.2.17",
62
+ "fs-extra": "^11.3.5",
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",
88
- "@types/fs-extra": "9.0.13",
85
+ "@types/debug": "4.1.13",
86
+ "@types/fs-extra": "11.0.4",
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",