client.mjs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. import '@vite/env';
  2. class HMRContext {
  3. constructor(hmrClient, ownerPath) {
  4. this.hmrClient = hmrClient;
  5. this.ownerPath = ownerPath;
  6. if (!hmrClient.dataMap.has(ownerPath)) {
  7. hmrClient.dataMap.set(ownerPath, {});
  8. }
  9. const mod = hmrClient.hotModulesMap.get(ownerPath);
  10. if (mod) {
  11. mod.callbacks = [];
  12. }
  13. const staleListeners = hmrClient.ctxToListenersMap.get(ownerPath);
  14. if (staleListeners) {
  15. for (const [event, staleFns] of staleListeners) {
  16. const listeners = hmrClient.customListenersMap.get(event);
  17. if (listeners) {
  18. hmrClient.customListenersMap.set(
  19. event,
  20. listeners.filter((l) => !staleFns.includes(l))
  21. );
  22. }
  23. }
  24. }
  25. this.newListeners = /* @__PURE__ */ new Map();
  26. hmrClient.ctxToListenersMap.set(ownerPath, this.newListeners);
  27. }
  28. get data() {
  29. return this.hmrClient.dataMap.get(this.ownerPath);
  30. }
  31. accept(deps, callback) {
  32. if (typeof deps === "function" || !deps) {
  33. this.acceptDeps([this.ownerPath], ([mod]) => deps?.(mod));
  34. } else if (typeof deps === "string") {
  35. this.acceptDeps([deps], ([mod]) => callback?.(mod));
  36. } else if (Array.isArray(deps)) {
  37. this.acceptDeps(deps, callback);
  38. } else {
  39. throw new Error(`invalid hot.accept() usage.`);
  40. }
  41. }
  42. // export names (first arg) are irrelevant on the client side, they're
  43. // extracted in the server for propagation
  44. acceptExports(_, callback) {
  45. this.acceptDeps([this.ownerPath], ([mod]) => callback?.(mod));
  46. }
  47. dispose(cb) {
  48. this.hmrClient.disposeMap.set(this.ownerPath, cb);
  49. }
  50. prune(cb) {
  51. this.hmrClient.pruneMap.set(this.ownerPath, cb);
  52. }
  53. // Kept for backward compatibility (#11036)
  54. // eslint-disable-next-line @typescript-eslint/no-empty-function
  55. decline() {
  56. }
  57. invalidate(message) {
  58. this.hmrClient.notifyListeners("vite:invalidate", {
  59. path: this.ownerPath,
  60. message
  61. });
  62. this.send("vite:invalidate", { path: this.ownerPath, message });
  63. this.hmrClient.logger.debug(
  64. `[vite] invalidate ${this.ownerPath}${message ? `: ${message}` : ""}`
  65. );
  66. }
  67. on(event, cb) {
  68. const addToMap = (map) => {
  69. const existing = map.get(event) || [];
  70. existing.push(cb);
  71. map.set(event, existing);
  72. };
  73. addToMap(this.hmrClient.customListenersMap);
  74. addToMap(this.newListeners);
  75. }
  76. off(event, cb) {
  77. const removeFromMap = (map) => {
  78. const existing = map.get(event);
  79. if (existing === void 0) {
  80. return;
  81. }
  82. const pruned = existing.filter((l) => l !== cb);
  83. if (pruned.length === 0) {
  84. map.delete(event);
  85. return;
  86. }
  87. map.set(event, pruned);
  88. };
  89. removeFromMap(this.hmrClient.customListenersMap);
  90. removeFromMap(this.newListeners);
  91. }
  92. send(event, data) {
  93. this.hmrClient.messenger.send(
  94. JSON.stringify({ type: "custom", event, data })
  95. );
  96. }
  97. acceptDeps(deps, callback = () => {
  98. }) {
  99. const mod = this.hmrClient.hotModulesMap.get(this.ownerPath) || {
  100. id: this.ownerPath,
  101. callbacks: []
  102. };
  103. mod.callbacks.push({
  104. deps,
  105. fn: callback
  106. });
  107. this.hmrClient.hotModulesMap.set(this.ownerPath, mod);
  108. }
  109. }
  110. class HMRMessenger {
  111. constructor(connection) {
  112. this.connection = connection;
  113. this.queue = [];
  114. }
  115. send(message) {
  116. this.queue.push(message);
  117. this.flush();
  118. }
  119. flush() {
  120. if (this.connection.isReady()) {
  121. this.queue.forEach((msg) => this.connection.send(msg));
  122. this.queue = [];
  123. }
  124. }
  125. }
  126. class HMRClient {
  127. constructor(logger, connection, importUpdatedModule) {
  128. this.logger = logger;
  129. this.importUpdatedModule = importUpdatedModule;
  130. this.hotModulesMap = /* @__PURE__ */ new Map();
  131. this.disposeMap = /* @__PURE__ */ new Map();
  132. this.pruneMap = /* @__PURE__ */ new Map();
  133. this.dataMap = /* @__PURE__ */ new Map();
  134. this.customListenersMap = /* @__PURE__ */ new Map();
  135. this.ctxToListenersMap = /* @__PURE__ */ new Map();
  136. this.updateQueue = [];
  137. this.pendingUpdateQueue = false;
  138. this.messenger = new HMRMessenger(connection);
  139. }
  140. async notifyListeners(event, data) {
  141. const cbs = this.customListenersMap.get(event);
  142. if (cbs) {
  143. await Promise.allSettled(cbs.map((cb) => cb(data)));
  144. }
  145. }
  146. clear() {
  147. this.hotModulesMap.clear();
  148. this.disposeMap.clear();
  149. this.pruneMap.clear();
  150. this.dataMap.clear();
  151. this.customListenersMap.clear();
  152. this.ctxToListenersMap.clear();
  153. }
  154. // After an HMR update, some modules are no longer imported on the page
  155. // but they may have left behind side effects that need to be cleaned up
  156. // (.e.g style injections)
  157. async prunePaths(paths) {
  158. await Promise.all(
  159. paths.map((path) => {
  160. const disposer = this.disposeMap.get(path);
  161. if (disposer) return disposer(this.dataMap.get(path));
  162. })
  163. );
  164. paths.forEach((path) => {
  165. const fn = this.pruneMap.get(path);
  166. if (fn) {
  167. fn(this.dataMap.get(path));
  168. }
  169. });
  170. }
  171. warnFailedUpdate(err, path) {
  172. if (!err.message.includes("fetch")) {
  173. this.logger.error(err);
  174. }
  175. this.logger.error(
  176. `[hmr] Failed to reload ${path}. This could be due to syntax errors or importing non-existent modules. (see errors above)`
  177. );
  178. }
  179. /**
  180. * buffer multiple hot updates triggered by the same src change
  181. * so that they are invoked in the same order they were sent.
  182. * (otherwise the order may be inconsistent because of the http request round trip)
  183. */
  184. async queueUpdate(payload) {
  185. this.updateQueue.push(this.fetchUpdate(payload));
  186. if (!this.pendingUpdateQueue) {
  187. this.pendingUpdateQueue = true;
  188. await Promise.resolve();
  189. this.pendingUpdateQueue = false;
  190. const loading = [...this.updateQueue];
  191. this.updateQueue = [];
  192. (await Promise.all(loading)).forEach((fn) => fn && fn());
  193. }
  194. }
  195. async fetchUpdate(update) {
  196. const { path, acceptedPath } = update;
  197. const mod = this.hotModulesMap.get(path);
  198. if (!mod) {
  199. return;
  200. }
  201. let fetchedModule;
  202. const isSelfUpdate = path === acceptedPath;
  203. const qualifiedCallbacks = mod.callbacks.filter(
  204. ({ deps }) => deps.includes(acceptedPath)
  205. );
  206. if (isSelfUpdate || qualifiedCallbacks.length > 0) {
  207. const disposer = this.disposeMap.get(acceptedPath);
  208. if (disposer) await disposer(this.dataMap.get(acceptedPath));
  209. try {
  210. fetchedModule = await this.importUpdatedModule(update);
  211. } catch (e) {
  212. this.warnFailedUpdate(e, acceptedPath);
  213. }
  214. }
  215. return () => {
  216. for (const { deps, fn } of qualifiedCallbacks) {
  217. fn(
  218. deps.map((dep) => dep === acceptedPath ? fetchedModule : void 0)
  219. );
  220. }
  221. const loggedPath = isSelfUpdate ? path : `${acceptedPath} via ${path}`;
  222. this.logger.debug(`[vite] hot updated: ${loggedPath}`);
  223. };
  224. }
  225. }
  226. const hmrConfigName = __HMR_CONFIG_NAME__;
  227. const base$1 = __BASE__ || "/";
  228. function h(e, attrs = {}, ...children) {
  229. const elem = document.createElement(e);
  230. for (const [k, v] of Object.entries(attrs)) {
  231. elem.setAttribute(k, v);
  232. }
  233. elem.append(...children);
  234. return elem;
  235. }
  236. const templateStyle = (
  237. /*css*/
  238. `
  239. :host {
  240. position: fixed;
  241. top: 0;
  242. left: 0;
  243. width: 100%;
  244. height: 100%;
  245. z-index: 99999;
  246. --monospace: 'SFMono-Regular', Consolas,
  247. 'Liberation Mono', Menlo, Courier, monospace;
  248. --red: #ff5555;
  249. --yellow: #e2aa53;
  250. --purple: #cfa4ff;
  251. --cyan: #2dd9da;
  252. --dim: #c9c9c9;
  253. --window-background: #181818;
  254. --window-color: #d8d8d8;
  255. }
  256. .backdrop {
  257. position: fixed;
  258. z-index: 99999;
  259. top: 0;
  260. left: 0;
  261. width: 100%;
  262. height: 100%;
  263. overflow-y: scroll;
  264. margin: 0;
  265. background: rgba(0, 0, 0, 0.66);
  266. }
  267. .window {
  268. font-family: var(--monospace);
  269. line-height: 1.5;
  270. max-width: 80vw;
  271. color: var(--window-color);
  272. box-sizing: border-box;
  273. margin: 30px auto;
  274. padding: 2.5vh 4vw;
  275. position: relative;
  276. background: var(--window-background);
  277. border-radius: 6px 6px 8px 8px;
  278. box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
  279. overflow: hidden;
  280. border-top: 8px solid var(--red);
  281. direction: ltr;
  282. text-align: left;
  283. }
  284. pre {
  285. font-family: var(--monospace);
  286. font-size: 16px;
  287. margin-top: 0;
  288. margin-bottom: 1em;
  289. overflow-x: scroll;
  290. scrollbar-width: none;
  291. }
  292. pre::-webkit-scrollbar {
  293. display: none;
  294. }
  295. pre.frame::-webkit-scrollbar {
  296. display: block;
  297. height: 5px;
  298. }
  299. pre.frame::-webkit-scrollbar-thumb {
  300. background: #999;
  301. border-radius: 5px;
  302. }
  303. pre.frame {
  304. scrollbar-width: thin;
  305. }
  306. .message {
  307. line-height: 1.3;
  308. font-weight: 600;
  309. white-space: pre-wrap;
  310. }
  311. .message-body {
  312. color: var(--red);
  313. }
  314. .plugin {
  315. color: var(--purple);
  316. }
  317. .file {
  318. color: var(--cyan);
  319. margin-bottom: 0;
  320. white-space: pre-wrap;
  321. word-break: break-all;
  322. }
  323. .frame {
  324. color: var(--yellow);
  325. }
  326. .stack {
  327. font-size: 13px;
  328. color: var(--dim);
  329. }
  330. .tip {
  331. font-size: 13px;
  332. color: #999;
  333. border-top: 1px dotted #999;
  334. padding-top: 13px;
  335. line-height: 1.8;
  336. }
  337. code {
  338. font-size: 13px;
  339. font-family: var(--monospace);
  340. color: var(--yellow);
  341. }
  342. .file-link {
  343. text-decoration: underline;
  344. cursor: pointer;
  345. }
  346. kbd {
  347. line-height: 1.5;
  348. font-family: ui-monospace, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  349. font-size: 0.75rem;
  350. font-weight: 700;
  351. background-color: rgb(38, 40, 44);
  352. color: rgb(166, 167, 171);
  353. padding: 0.15rem 0.3rem;
  354. border-radius: 0.25rem;
  355. border-width: 0.0625rem 0.0625rem 0.1875rem;
  356. border-style: solid;
  357. border-color: rgb(54, 57, 64);
  358. border-image: initial;
  359. }
  360. `
  361. );
  362. const createTemplate = () => h(
  363. "div",
  364. { class: "backdrop", part: "backdrop" },
  365. h(
  366. "div",
  367. { class: "window", part: "window" },
  368. h(
  369. "pre",
  370. { class: "message", part: "message" },
  371. h("span", { class: "plugin", part: "plugin" }),
  372. h("span", { class: "message-body", part: "message-body" })
  373. ),
  374. h("pre", { class: "file", part: "file" }),
  375. h("pre", { class: "frame", part: "frame" }),
  376. h("pre", { class: "stack", part: "stack" }),
  377. h(
  378. "div",
  379. { class: "tip", part: "tip" },
  380. "Click outside, press ",
  381. h("kbd", {}, "Esc"),
  382. " key, or fix the code to dismiss.",
  383. h("br"),
  384. "You can also disable this overlay by setting ",
  385. h("code", { part: "config-option-name" }, "server.hmr.overlay"),
  386. " to ",
  387. h("code", { part: "config-option-value" }, "false"),
  388. " in ",
  389. h("code", { part: "config-file-name" }, hmrConfigName),
  390. "."
  391. )
  392. ),
  393. h("style", {}, templateStyle)
  394. );
  395. const fileRE = /(?:[a-zA-Z]:\\|\/).*?:\d+:\d+/g;
  396. const codeframeRE = /^(?:>?\s*\d+\s+\|.*|\s+\|\s*\^.*)\r?\n/gm;
  397. const { HTMLElement = class {
  398. } } = globalThis;
  399. class ErrorOverlay extends HTMLElement {
  400. constructor(err, links = true) {
  401. super();
  402. this.root = this.attachShadow({ mode: "open" });
  403. this.root.appendChild(createTemplate());
  404. codeframeRE.lastIndex = 0;
  405. const hasFrame = err.frame && codeframeRE.test(err.frame);
  406. const message = hasFrame ? err.message.replace(codeframeRE, "") : err.message;
  407. if (err.plugin) {
  408. this.text(".plugin", `[plugin:${err.plugin}] `);
  409. }
  410. this.text(".message-body", message.trim());
  411. const [file] = (err.loc?.file || err.id || "unknown file").split(`?`);
  412. if (err.loc) {
  413. this.text(".file", `${file}:${err.loc.line}:${err.loc.column}`, links);
  414. } else if (err.id) {
  415. this.text(".file", file);
  416. }
  417. if (hasFrame) {
  418. this.text(".frame", err.frame.trim());
  419. }
  420. this.text(".stack", err.stack, links);
  421. this.root.querySelector(".window").addEventListener("click", (e) => {
  422. e.stopPropagation();
  423. });
  424. this.addEventListener("click", () => {
  425. this.close();
  426. });
  427. this.closeOnEsc = (e) => {
  428. if (e.key === "Escape" || e.code === "Escape") {
  429. this.close();
  430. }
  431. };
  432. document.addEventListener("keydown", this.closeOnEsc);
  433. }
  434. text(selector, text, linkFiles = false) {
  435. const el = this.root.querySelector(selector);
  436. if (!linkFiles) {
  437. el.textContent = text;
  438. } else {
  439. let curIndex = 0;
  440. let match;
  441. fileRE.lastIndex = 0;
  442. while (match = fileRE.exec(text)) {
  443. const { 0: file, index } = match;
  444. if (index != null) {
  445. const frag = text.slice(curIndex, index);
  446. el.appendChild(document.createTextNode(frag));
  447. const link = document.createElement("a");
  448. link.textContent = file;
  449. link.className = "file-link";
  450. link.onclick = () => {
  451. fetch(
  452. new URL(
  453. `${base$1}__open-in-editor?file=${encodeURIComponent(file)}`,
  454. import.meta.url
  455. )
  456. );
  457. };
  458. el.appendChild(link);
  459. curIndex += frag.length + file.length;
  460. }
  461. }
  462. }
  463. }
  464. close() {
  465. this.parentNode?.removeChild(this);
  466. document.removeEventListener("keydown", this.closeOnEsc);
  467. }
  468. }
  469. const overlayId = "vite-error-overlay";
  470. const { customElements } = globalThis;
  471. if (customElements && !customElements.get(overlayId)) {
  472. customElements.define(overlayId, ErrorOverlay);
  473. }
  474. console.debug("[vite] connecting...");
  475. const importMetaUrl = new URL(import.meta.url);
  476. const serverHost = __SERVER_HOST__;
  477. const socketProtocol = __HMR_PROTOCOL__ || (importMetaUrl.protocol === "https:" ? "wss" : "ws");
  478. const hmrPort = __HMR_PORT__;
  479. const socketHost = `${__HMR_HOSTNAME__ || importMetaUrl.hostname}:${hmrPort || importMetaUrl.port}${__HMR_BASE__}`;
  480. const directSocketHost = __HMR_DIRECT_TARGET__;
  481. const base = __BASE__ || "/";
  482. let socket;
  483. try {
  484. let fallback;
  485. if (!hmrPort) {
  486. fallback = () => {
  487. socket = setupWebSocket(socketProtocol, directSocketHost, () => {
  488. const currentScriptHostURL = new URL(import.meta.url);
  489. const currentScriptHost = currentScriptHostURL.host + currentScriptHostURL.pathname.replace(/@vite\/client$/, "");
  490. console.error(
  491. `[vite] failed to connect to websocket.
  492. your current setup:
  493. (browser) ${currentScriptHost} <--[HTTP]--> ${serverHost} (server)
  494. (browser) ${socketHost} <--[WebSocket (failing)]--> ${directSocketHost} (server)
  495. Check out your Vite / network configuration and https://vite.dev/config/server-options.html#server-hmr .`
  496. );
  497. });
  498. socket.addEventListener(
  499. "open",
  500. () => {
  501. console.info(
  502. "[vite] Direct websocket connection fallback. Check out https://vite.dev/config/server-options.html#server-hmr to remove the previous connection error."
  503. );
  504. },
  505. { once: true }
  506. );
  507. };
  508. }
  509. socket = setupWebSocket(socketProtocol, socketHost, fallback);
  510. } catch (error) {
  511. console.error(`[vite] failed to connect to websocket (${error}). `);
  512. }
  513. function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {
  514. const socket2 = new WebSocket(`${protocol}://${hostAndPath}`, "vite-hmr");
  515. let isOpened = false;
  516. socket2.addEventListener(
  517. "open",
  518. () => {
  519. isOpened = true;
  520. notifyListeners("vite:ws:connect", { webSocket: socket2 });
  521. },
  522. { once: true }
  523. );
  524. socket2.addEventListener("message", async ({ data }) => {
  525. handleMessage(JSON.parse(data));
  526. });
  527. socket2.addEventListener("close", async ({ wasClean }) => {
  528. if (wasClean) return;
  529. if (!isOpened && onCloseWithoutOpen) {
  530. onCloseWithoutOpen();
  531. return;
  532. }
  533. notifyListeners("vite:ws:disconnect", { webSocket: socket2 });
  534. if (hasDocument) {
  535. console.log(`[vite] server connection lost. Polling for restart...`);
  536. await waitForSuccessfulPing(protocol, hostAndPath);
  537. location.reload();
  538. }
  539. });
  540. return socket2;
  541. }
  542. function cleanUrl(pathname) {
  543. const url = new URL(pathname, "http://vite.dev");
  544. url.searchParams.delete("direct");
  545. return url.pathname + url.search;
  546. }
  547. let isFirstUpdate = true;
  548. const outdatedLinkTags = /* @__PURE__ */ new WeakSet();
  549. const debounceReload = (time) => {
  550. let timer;
  551. return () => {
  552. if (timer) {
  553. clearTimeout(timer);
  554. timer = null;
  555. }
  556. timer = setTimeout(() => {
  557. location.reload();
  558. }, time);
  559. };
  560. };
  561. const pageReload = debounceReload(50);
  562. const hmrClient = new HMRClient(
  563. console,
  564. {
  565. isReady: () => socket && socket.readyState === 1,
  566. send: (message) => socket.send(message)
  567. },
  568. async function importUpdatedModule({
  569. acceptedPath,
  570. timestamp,
  571. explicitImportRequired,
  572. isWithinCircularImport
  573. }) {
  574. const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`);
  575. const importPromise = import(
  576. /* @vite-ignore */
  577. base + acceptedPathWithoutQuery.slice(1) + `?${explicitImportRequired ? "import&" : ""}t=${timestamp}${query ? `&${query}` : ""}`
  578. );
  579. if (isWithinCircularImport) {
  580. importPromise.catch(() => {
  581. console.info(
  582. `[hmr] ${acceptedPath} failed to apply HMR as it's within a circular import. Reloading page to reset the execution order. To debug and break the circular import, you can run \`vite --debug hmr\` to log the circular dependency path if a file change triggered it.`
  583. );
  584. pageReload();
  585. });
  586. }
  587. return await importPromise;
  588. }
  589. );
  590. async function handleMessage(payload) {
  591. switch (payload.type) {
  592. case "connected":
  593. console.debug(`[vite] connected.`);
  594. hmrClient.messenger.flush();
  595. setInterval(() => {
  596. if (socket.readyState === socket.OPEN) {
  597. socket.send('{"type":"ping"}');
  598. }
  599. }, __HMR_TIMEOUT__);
  600. break;
  601. case "update":
  602. notifyListeners("vite:beforeUpdate", payload);
  603. if (hasDocument) {
  604. if (isFirstUpdate && hasErrorOverlay()) {
  605. location.reload();
  606. return;
  607. } else {
  608. if (enableOverlay) {
  609. clearErrorOverlay();
  610. }
  611. isFirstUpdate = false;
  612. }
  613. }
  614. await Promise.all(
  615. payload.updates.map(async (update) => {
  616. if (update.type === "js-update") {
  617. return hmrClient.queueUpdate(update);
  618. }
  619. const { path, timestamp } = update;
  620. const searchUrl = cleanUrl(path);
  621. const el = Array.from(
  622. document.querySelectorAll("link")
  623. ).find(
  624. (e) => !outdatedLinkTags.has(e) && cleanUrl(e.href).includes(searchUrl)
  625. );
  626. if (!el) {
  627. return;
  628. }
  629. const newPath = `${base}${searchUrl.slice(1)}${searchUrl.includes("?") ? "&" : "?"}t=${timestamp}`;
  630. return new Promise((resolve) => {
  631. const newLinkTag = el.cloneNode();
  632. newLinkTag.href = new URL(newPath, el.href).href;
  633. const removeOldEl = () => {
  634. el.remove();
  635. console.debug(`[vite] css hot updated: ${searchUrl}`);
  636. resolve();
  637. };
  638. newLinkTag.addEventListener("load", removeOldEl);
  639. newLinkTag.addEventListener("error", removeOldEl);
  640. outdatedLinkTags.add(el);
  641. el.after(newLinkTag);
  642. });
  643. })
  644. );
  645. notifyListeners("vite:afterUpdate", payload);
  646. break;
  647. case "custom": {
  648. notifyListeners(payload.event, payload.data);
  649. break;
  650. }
  651. case "full-reload":
  652. notifyListeners("vite:beforeFullReload", payload);
  653. if (hasDocument) {
  654. if (payload.path && payload.path.endsWith(".html")) {
  655. const pagePath = decodeURI(location.pathname);
  656. const payloadPath = base + payload.path.slice(1);
  657. if (pagePath === payloadPath || payload.path === "/index.html" || pagePath.endsWith("/") && pagePath + "index.html" === payloadPath) {
  658. pageReload();
  659. }
  660. return;
  661. } else {
  662. pageReload();
  663. }
  664. }
  665. break;
  666. case "prune":
  667. notifyListeners("vite:beforePrune", payload);
  668. await hmrClient.prunePaths(payload.paths);
  669. break;
  670. case "error": {
  671. notifyListeners("vite:error", payload);
  672. if (hasDocument) {
  673. const err = payload.err;
  674. if (enableOverlay) {
  675. createErrorOverlay(err);
  676. } else {
  677. console.error(
  678. `[vite] Internal Server Error
  679. ${err.message}
  680. ${err.stack}`
  681. );
  682. }
  683. }
  684. break;
  685. }
  686. default: {
  687. const check = payload;
  688. return check;
  689. }
  690. }
  691. }
  692. function notifyListeners(event, data) {
  693. hmrClient.notifyListeners(event, data);
  694. }
  695. const enableOverlay = __HMR_ENABLE_OVERLAY__;
  696. const hasDocument = "document" in globalThis;
  697. function createErrorOverlay(err) {
  698. clearErrorOverlay();
  699. document.body.appendChild(new ErrorOverlay(err));
  700. }
  701. function clearErrorOverlay() {
  702. document.querySelectorAll(overlayId).forEach((n) => n.close());
  703. }
  704. function hasErrorOverlay() {
  705. return document.querySelectorAll(overlayId).length;
  706. }
  707. async function waitForSuccessfulPing(socketProtocol2, hostAndPath, ms = 1e3) {
  708. const pingHostProtocol = socketProtocol2 === "wss" ? "https" : "http";
  709. const ping = async () => {
  710. try {
  711. await fetch(`${pingHostProtocol}://${hostAndPath}`, {
  712. mode: "no-cors",
  713. headers: {
  714. // Custom headers won't be included in a request with no-cors so (ab)use one of the
  715. // safelisted headers to identify the ping request
  716. Accept: "text/x-vite-ping"
  717. }
  718. });
  719. return true;
  720. } catch {
  721. }
  722. return false;
  723. };
  724. if (await ping()) {
  725. return;
  726. }
  727. await wait(ms);
  728. while (true) {
  729. if (document.visibilityState === "visible") {
  730. if (await ping()) {
  731. break;
  732. }
  733. await wait(ms);
  734. } else {
  735. await waitForWindowShow();
  736. }
  737. }
  738. }
  739. function wait(ms) {
  740. return new Promise((resolve) => setTimeout(resolve, ms));
  741. }
  742. function waitForWindowShow() {
  743. return new Promise((resolve) => {
  744. const onChange = async () => {
  745. if (document.visibilityState === "visible") {
  746. resolve();
  747. document.removeEventListener("visibilitychange", onChange);
  748. }
  749. };
  750. document.addEventListener("visibilitychange", onChange);
  751. });
  752. }
  753. const sheetsMap = /* @__PURE__ */ new Map();
  754. if ("document" in globalThis) {
  755. document.querySelectorAll("style[data-vite-dev-id]").forEach((el) => {
  756. sheetsMap.set(el.getAttribute("data-vite-dev-id"), el);
  757. });
  758. }
  759. const cspNonce = "document" in globalThis ? document.querySelector("meta[property=csp-nonce]")?.nonce : void 0;
  760. let lastInsertedStyle;
  761. function updateStyle(id, content) {
  762. let style = sheetsMap.get(id);
  763. if (!style) {
  764. style = document.createElement("style");
  765. style.setAttribute("type", "text/css");
  766. style.setAttribute("data-vite-dev-id", id);
  767. style.textContent = content;
  768. if (cspNonce) {
  769. style.setAttribute("nonce", cspNonce);
  770. }
  771. if (!lastInsertedStyle) {
  772. document.head.appendChild(style);
  773. setTimeout(() => {
  774. lastInsertedStyle = void 0;
  775. }, 0);
  776. } else {
  777. lastInsertedStyle.insertAdjacentElement("afterend", style);
  778. }
  779. lastInsertedStyle = style;
  780. } else {
  781. style.textContent = content;
  782. }
  783. sheetsMap.set(id, style);
  784. }
  785. function removeStyle(id) {
  786. const style = sheetsMap.get(id);
  787. if (style) {
  788. document.head.removeChild(style);
  789. sheetsMap.delete(id);
  790. }
  791. }
  792. function createHotContext(ownerPath) {
  793. return new HMRContext(hmrClient, ownerPath);
  794. }
  795. function injectQuery(url, queryToInject) {
  796. if (url[0] !== "." && url[0] !== "/") {
  797. return url;
  798. }
  799. const pathname = url.replace(/[?#].*$/, "");
  800. const { search, hash } = new URL(url, "http://vite.dev");
  801. return `${pathname}?${queryToInject}${search ? `&` + search.slice(1) : ""}${hash || ""}`;
  802. }
  803. export { ErrorOverlay, createHotContext, injectQuery, removeStyle, updateStyle };