client.mjs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  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. const wsToken = __WS_TOKEN__;
  483. let socket;
  484. try {
  485. let fallback;
  486. if (!hmrPort) {
  487. fallback = () => {
  488. socket = setupWebSocket(socketProtocol, directSocketHost, () => {
  489. const currentScriptHostURL = new URL(import.meta.url);
  490. const currentScriptHost = currentScriptHostURL.host + currentScriptHostURL.pathname.replace(/@vite\/client$/, "");
  491. console.error(
  492. `[vite] failed to connect to websocket.
  493. your current setup:
  494. (browser) ${currentScriptHost} <--[HTTP]--> ${serverHost} (server)
  495. (browser) ${socketHost} <--[WebSocket (failing)]--> ${directSocketHost} (server)
  496. Check out your Vite / network configuration and https://vite.dev/config/server-options.html#server-hmr .`
  497. );
  498. });
  499. socket.addEventListener(
  500. "open",
  501. () => {
  502. console.info(
  503. "[vite] Direct websocket connection fallback. Check out https://vite.dev/config/server-options.html#server-hmr to remove the previous connection error."
  504. );
  505. },
  506. { once: true }
  507. );
  508. };
  509. }
  510. socket = setupWebSocket(socketProtocol, socketHost, fallback);
  511. } catch (error) {
  512. console.error(`[vite] failed to connect to websocket (${error}). `);
  513. }
  514. function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {
  515. const socket2 = new WebSocket(
  516. `${protocol}://${hostAndPath}?token=${wsToken}`,
  517. "vite-hmr"
  518. );
  519. let isOpened = false;
  520. socket2.addEventListener(
  521. "open",
  522. () => {
  523. isOpened = true;
  524. notifyListeners("vite:ws:connect", { webSocket: socket2 });
  525. },
  526. { once: true }
  527. );
  528. socket2.addEventListener("message", async ({ data }) => {
  529. handleMessage(JSON.parse(data));
  530. });
  531. socket2.addEventListener("close", async ({ wasClean }) => {
  532. if (wasClean) return;
  533. if (!isOpened && onCloseWithoutOpen) {
  534. onCloseWithoutOpen();
  535. return;
  536. }
  537. notifyListeners("vite:ws:disconnect", { webSocket: socket2 });
  538. if (hasDocument) {
  539. console.log(`[vite] server connection lost. Polling for restart...`);
  540. await waitForSuccessfulPing(protocol, hostAndPath);
  541. location.reload();
  542. }
  543. });
  544. return socket2;
  545. }
  546. function cleanUrl(pathname) {
  547. const url = new URL(pathname, "http://vite.dev");
  548. url.searchParams.delete("direct");
  549. return url.pathname + url.search;
  550. }
  551. let isFirstUpdate = true;
  552. const outdatedLinkTags = /* @__PURE__ */ new WeakSet();
  553. const debounceReload = (time) => {
  554. let timer;
  555. return () => {
  556. if (timer) {
  557. clearTimeout(timer);
  558. timer = null;
  559. }
  560. timer = setTimeout(() => {
  561. location.reload();
  562. }, time);
  563. };
  564. };
  565. const pageReload = debounceReload(50);
  566. const hmrClient = new HMRClient(
  567. console,
  568. {
  569. isReady: () => socket && socket.readyState === 1,
  570. send: (message) => socket.send(message)
  571. },
  572. async function importUpdatedModule({
  573. acceptedPath,
  574. timestamp,
  575. explicitImportRequired,
  576. isWithinCircularImport
  577. }) {
  578. const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`);
  579. const importPromise = import(
  580. /* @vite-ignore */
  581. base + acceptedPathWithoutQuery.slice(1) + `?${explicitImportRequired ? "import&" : ""}t=${timestamp}${query ? `&${query}` : ""}`
  582. );
  583. if (isWithinCircularImport) {
  584. importPromise.catch(() => {
  585. console.info(
  586. `[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.`
  587. );
  588. pageReload();
  589. });
  590. }
  591. return await importPromise;
  592. }
  593. );
  594. async function handleMessage(payload) {
  595. switch (payload.type) {
  596. case "connected":
  597. console.debug(`[vite] connected.`);
  598. hmrClient.messenger.flush();
  599. setInterval(() => {
  600. if (socket.readyState === socket.OPEN) {
  601. socket.send('{"type":"ping"}');
  602. }
  603. }, __HMR_TIMEOUT__);
  604. break;
  605. case "update":
  606. notifyListeners("vite:beforeUpdate", payload);
  607. if (hasDocument) {
  608. if (isFirstUpdate && hasErrorOverlay()) {
  609. location.reload();
  610. return;
  611. } else {
  612. if (enableOverlay) {
  613. clearErrorOverlay();
  614. }
  615. isFirstUpdate = false;
  616. }
  617. }
  618. await Promise.all(
  619. payload.updates.map(async (update) => {
  620. if (update.type === "js-update") {
  621. return hmrClient.queueUpdate(update);
  622. }
  623. const { path, timestamp } = update;
  624. const searchUrl = cleanUrl(path);
  625. const el = Array.from(
  626. document.querySelectorAll("link")
  627. ).find(
  628. (e) => !outdatedLinkTags.has(e) && cleanUrl(e.href).includes(searchUrl)
  629. );
  630. if (!el) {
  631. return;
  632. }
  633. const newPath = `${base}${searchUrl.slice(1)}${searchUrl.includes("?") ? "&" : "?"}t=${timestamp}`;
  634. return new Promise((resolve) => {
  635. const newLinkTag = el.cloneNode();
  636. newLinkTag.href = new URL(newPath, el.href).href;
  637. const removeOldEl = () => {
  638. el.remove();
  639. console.debug(`[vite] css hot updated: ${searchUrl}`);
  640. resolve();
  641. };
  642. newLinkTag.addEventListener("load", removeOldEl);
  643. newLinkTag.addEventListener("error", removeOldEl);
  644. outdatedLinkTags.add(el);
  645. el.after(newLinkTag);
  646. });
  647. })
  648. );
  649. notifyListeners("vite:afterUpdate", payload);
  650. break;
  651. case "custom": {
  652. notifyListeners(payload.event, payload.data);
  653. break;
  654. }
  655. case "full-reload":
  656. notifyListeners("vite:beforeFullReload", payload);
  657. if (hasDocument) {
  658. if (payload.path && payload.path.endsWith(".html")) {
  659. const pagePath = decodeURI(location.pathname);
  660. const payloadPath = base + payload.path.slice(1);
  661. if (pagePath === payloadPath || payload.path === "/index.html" || pagePath.endsWith("/") && pagePath + "index.html" === payloadPath) {
  662. pageReload();
  663. }
  664. return;
  665. } else {
  666. pageReload();
  667. }
  668. }
  669. break;
  670. case "prune":
  671. notifyListeners("vite:beforePrune", payload);
  672. await hmrClient.prunePaths(payload.paths);
  673. break;
  674. case "error": {
  675. notifyListeners("vite:error", payload);
  676. if (hasDocument) {
  677. const err = payload.err;
  678. if (enableOverlay) {
  679. createErrorOverlay(err);
  680. } else {
  681. console.error(
  682. `[vite] Internal Server Error
  683. ${err.message}
  684. ${err.stack}`
  685. );
  686. }
  687. }
  688. break;
  689. }
  690. default: {
  691. const check = payload;
  692. return check;
  693. }
  694. }
  695. }
  696. function notifyListeners(event, data) {
  697. hmrClient.notifyListeners(event, data);
  698. }
  699. const enableOverlay = __HMR_ENABLE_OVERLAY__;
  700. const hasDocument = "document" in globalThis;
  701. function createErrorOverlay(err) {
  702. clearErrorOverlay();
  703. document.body.appendChild(new ErrorOverlay(err));
  704. }
  705. function clearErrorOverlay() {
  706. document.querySelectorAll(overlayId).forEach((n) => n.close());
  707. }
  708. function hasErrorOverlay() {
  709. return document.querySelectorAll(overlayId).length;
  710. }
  711. async function waitForSuccessfulPing(socketProtocol2, hostAndPath, ms = 1e3) {
  712. const pingHostProtocol = socketProtocol2 === "wss" ? "https" : "http";
  713. const ping = async () => {
  714. try {
  715. await fetch(`${pingHostProtocol}://${hostAndPath}`, {
  716. mode: "no-cors",
  717. headers: {
  718. // Custom headers won't be included in a request with no-cors so (ab)use one of the
  719. // safelisted headers to identify the ping request
  720. Accept: "text/x-vite-ping"
  721. }
  722. });
  723. return true;
  724. } catch {
  725. }
  726. return false;
  727. };
  728. if (await ping()) {
  729. return;
  730. }
  731. await wait(ms);
  732. while (true) {
  733. if (document.visibilityState === "visible") {
  734. if (await ping()) {
  735. break;
  736. }
  737. await wait(ms);
  738. } else {
  739. await waitForWindowShow();
  740. }
  741. }
  742. }
  743. function wait(ms) {
  744. return new Promise((resolve) => setTimeout(resolve, ms));
  745. }
  746. function waitForWindowShow() {
  747. return new Promise((resolve) => {
  748. const onChange = async () => {
  749. if (document.visibilityState === "visible") {
  750. resolve();
  751. document.removeEventListener("visibilitychange", onChange);
  752. }
  753. };
  754. document.addEventListener("visibilitychange", onChange);
  755. });
  756. }
  757. const sheetsMap = /* @__PURE__ */ new Map();
  758. if ("document" in globalThis) {
  759. document.querySelectorAll("style[data-vite-dev-id]").forEach((el) => {
  760. sheetsMap.set(el.getAttribute("data-vite-dev-id"), el);
  761. });
  762. }
  763. const cspNonce = "document" in globalThis ? document.querySelector("meta[property=csp-nonce]")?.nonce : void 0;
  764. let lastInsertedStyle;
  765. function updateStyle(id, content) {
  766. let style = sheetsMap.get(id);
  767. if (!style) {
  768. style = document.createElement("style");
  769. style.setAttribute("type", "text/css");
  770. style.setAttribute("data-vite-dev-id", id);
  771. style.textContent = content;
  772. if (cspNonce) {
  773. style.setAttribute("nonce", cspNonce);
  774. }
  775. if (!lastInsertedStyle) {
  776. document.head.appendChild(style);
  777. setTimeout(() => {
  778. lastInsertedStyle = void 0;
  779. }, 0);
  780. } else {
  781. lastInsertedStyle.insertAdjacentElement("afterend", style);
  782. }
  783. lastInsertedStyle = style;
  784. } else {
  785. style.textContent = content;
  786. }
  787. sheetsMap.set(id, style);
  788. }
  789. function removeStyle(id) {
  790. const style = sheetsMap.get(id);
  791. if (style) {
  792. document.head.removeChild(style);
  793. sheetsMap.delete(id);
  794. }
  795. }
  796. function createHotContext(ownerPath) {
  797. return new HMRContext(hmrClient, ownerPath);
  798. }
  799. function injectQuery(url, queryToInject) {
  800. if (url[0] !== "." && url[0] !== "/") {
  801. return url;
  802. }
  803. const pathname = url.replace(/[?#].*$/, "");
  804. const { search, hash } = new URL(url, "http://vite.dev");
  805. return `${pathname}?${queryToInject}${search ? `&` + search.slice(1) : ""}${hash || ""}`;
  806. }
  807. export { ErrorOverlay, createHotContext, injectQuery, removeStyle, updateStyle };