client.mjs 25 KB

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