646 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			646 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import '@vite/env';
 | |
| 
 | |
| // set :host styles to make playwright detect the element as visible
 | |
| const template = /*html*/ `
 | |
| <style>
 | |
| :host {
 | |
|   position: fixed;
 | |
|   top: 0;
 | |
|   left: 0;
 | |
|   width: 100%;
 | |
|   height: 100%;
 | |
|   z-index: 99999;
 | |
|   --monospace: 'SFMono-Regular', Consolas,
 | |
|   'Liberation Mono', Menlo, Courier, monospace;
 | |
|   --red: #ff5555;
 | |
|   --yellow: #e2aa53;
 | |
|   --purple: #cfa4ff;
 | |
|   --cyan: #2dd9da;
 | |
|   --dim: #c9c9c9;
 | |
| 
 | |
|   --window-background: #181818;
 | |
|   --window-color: #d8d8d8;
 | |
| }
 | |
| 
 | |
| .backdrop {
 | |
|   position: fixed;
 | |
|   z-index: 99999;
 | |
|   top: 0;
 | |
|   left: 0;
 | |
|   width: 100%;
 | |
|   height: 100%;
 | |
|   overflow-y: scroll;
 | |
|   margin: 0;
 | |
|   background: rgba(0, 0, 0, 0.66);
 | |
| }
 | |
| 
 | |
| .window {
 | |
|   font-family: var(--monospace);
 | |
|   line-height: 1.5;
 | |
|   width: 800px;
 | |
|   color: var(--window-color);
 | |
|   margin: 30px auto;
 | |
|   padding: 25px 40px;
 | |
|   position: relative;
 | |
|   background: var(--window-background);
 | |
|   border-radius: 6px 6px 8px 8px;
 | |
|   box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
 | |
|   overflow: hidden;
 | |
|   border-top: 8px solid var(--red);
 | |
|   direction: ltr;
 | |
|   text-align: left;
 | |
| }
 | |
| 
 | |
| pre {
 | |
|   font-family: var(--monospace);
 | |
|   font-size: 16px;
 | |
|   margin-top: 0;
 | |
|   margin-bottom: 1em;
 | |
|   overflow-x: scroll;
 | |
|   scrollbar-width: none;
 | |
| }
 | |
| 
 | |
| pre::-webkit-scrollbar {
 | |
|   display: none;
 | |
| }
 | |
| 
 | |
| .message {
 | |
|   line-height: 1.3;
 | |
|   font-weight: 600;
 | |
|   white-space: pre-wrap;
 | |
| }
 | |
| 
 | |
| .message-body {
 | |
|   color: var(--red);
 | |
| }
 | |
| 
 | |
| .plugin {
 | |
|   color: var(--purple);
 | |
| }
 | |
| 
 | |
| .file {
 | |
|   color: var(--cyan);
 | |
|   margin-bottom: 0;
 | |
|   white-space: pre-wrap;
 | |
|   word-break: break-all;
 | |
| }
 | |
| 
 | |
| .frame {
 | |
|   color: var(--yellow);
 | |
| }
 | |
| 
 | |
| .stack {
 | |
|   font-size: 13px;
 | |
|   color: var(--dim);
 | |
| }
 | |
| 
 | |
| .tip {
 | |
|   font-size: 13px;
 | |
|   color: #999;
 | |
|   border-top: 1px dotted #999;
 | |
|   padding-top: 13px;
 | |
| }
 | |
| 
 | |
| code {
 | |
|   font-size: 13px;
 | |
|   font-family: var(--monospace);
 | |
|   color: var(--yellow);
 | |
| }
 | |
| 
 | |
| .file-link {
 | |
|   text-decoration: underline;
 | |
|   cursor: pointer;
 | |
| }
 | |
| </style>
 | |
| <div class="backdrop" part="backdrop">
 | |
|   <div class="window" part="window">
 | |
|     <pre class="message" part="message"><span class="plugin"></span><span class="message-body"></span></pre>
 | |
|     <pre class="file" part="file"></pre>
 | |
|     <pre class="frame" part="frame"></pre>
 | |
|     <pre class="stack" part="stack"></pre>
 | |
|     <div class="tip" part="tip">
 | |
|       Click outside or fix the code to dismiss.<br>
 | |
|       You can also disable this overlay by setting
 | |
|       <code>server.hmr.overlay</code> to <code>false</code> in <code>vite.config.js.</code>
 | |
|     </div>
 | |
|   </div>
 | |
| </div>
 | |
| `;
 | |
| const fileRE = /(?:[a-zA-Z]:\\|\/).*?:\d+:\d+/g;
 | |
| const codeframeRE = /^(?:>?\s+\d+\s+\|.*|\s+\|\s*\^.*)\r?\n/gm;
 | |
| // Allow `ErrorOverlay` to extend `HTMLElement` even in environments where
 | |
| // `HTMLElement` was not originally defined.
 | |
| const { HTMLElement = class {
 | |
| } } = globalThis;
 | |
| class ErrorOverlay extends HTMLElement {
 | |
|     constructor(err, links = true) {
 | |
|         var _a;
 | |
|         super();
 | |
|         this.root = this.attachShadow({ mode: 'open' });
 | |
|         this.root.innerHTML = template;
 | |
|         codeframeRE.lastIndex = 0;
 | |
|         const hasFrame = err.frame && codeframeRE.test(err.frame);
 | |
|         const message = hasFrame
 | |
|             ? err.message.replace(codeframeRE, '')
 | |
|             : err.message;
 | |
|         if (err.plugin) {
 | |
|             this.text('.plugin', `[plugin:${err.plugin}] `);
 | |
|         }
 | |
|         this.text('.message-body', message.trim());
 | |
|         const [file] = (((_a = err.loc) === null || _a === void 0 ? void 0 : _a.file) || err.id || 'unknown file').split(`?`);
 | |
|         if (err.loc) {
 | |
|             this.text('.file', `${file}:${err.loc.line}:${err.loc.column}`, links);
 | |
|         }
 | |
|         else if (err.id) {
 | |
|             this.text('.file', file);
 | |
|         }
 | |
|         if (hasFrame) {
 | |
|             this.text('.frame', err.frame.trim());
 | |
|         }
 | |
|         this.text('.stack', err.stack, links);
 | |
|         this.root.querySelector('.window').addEventListener('click', (e) => {
 | |
|             e.stopPropagation();
 | |
|         });
 | |
|         this.addEventListener('click', () => {
 | |
|             this.close();
 | |
|         });
 | |
|     }
 | |
|     text(selector, text, linkFiles = false) {
 | |
|         const el = this.root.querySelector(selector);
 | |
|         if (!linkFiles) {
 | |
|             el.textContent = text;
 | |
|         }
 | |
|         else {
 | |
|             let curIndex = 0;
 | |
|             let match;
 | |
|             fileRE.lastIndex = 0;
 | |
|             while ((match = fileRE.exec(text))) {
 | |
|                 const { 0: file, index } = match;
 | |
|                 if (index != null) {
 | |
|                     const frag = text.slice(curIndex, index);
 | |
|                     el.appendChild(document.createTextNode(frag));
 | |
|                     const link = document.createElement('a');
 | |
|                     link.textContent = file;
 | |
|                     link.className = 'file-link';
 | |
|                     link.onclick = () => {
 | |
|                         fetch('/__open-in-editor?file=' + encodeURIComponent(file));
 | |
|                     };
 | |
|                     el.appendChild(link);
 | |
|                     curIndex += frag.length + file.length;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     close() {
 | |
|         var _a;
 | |
|         (_a = this.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this);
 | |
|     }
 | |
| }
 | |
| const overlayId = 'vite-error-overlay';
 | |
| const { customElements } = globalThis; // Ensure `customElements` is defined before the next line.
 | |
| if (customElements && !customElements.get(overlayId)) {
 | |
|     customElements.define(overlayId, ErrorOverlay);
 | |
| }
 | |
| 
 | |
| console.debug('[vite] connecting...');
 | |
| const importMetaUrl = new URL(import.meta.url);
 | |
| // use server configuration, then fallback to inference
 | |
| const serverHost = __SERVER_HOST__;
 | |
| const socketProtocol = __HMR_PROTOCOL__ || (location.protocol === 'https:' ? 'wss' : 'ws');
 | |
| const hmrPort = __HMR_PORT__;
 | |
| const socketHost = `${__HMR_HOSTNAME__ || importMetaUrl.hostname}:${hmrPort || importMetaUrl.port}${__HMR_BASE__}`;
 | |
| const directSocketHost = __HMR_DIRECT_TARGET__;
 | |
| const base = __BASE__ || '/';
 | |
| const messageBuffer = [];
 | |
| let socket;
 | |
| try {
 | |
|     let fallback;
 | |
|     // only use fallback when port is inferred to prevent confusion
 | |
|     if (!hmrPort) {
 | |
|         fallback = () => {
 | |
|             // fallback to connecting directly to the hmr server
 | |
|             // for servers which does not support proxying websocket
 | |
|             socket = setupWebSocket(socketProtocol, directSocketHost, () => {
 | |
|                 const currentScriptHostURL = new URL(import.meta.url);
 | |
|                 const currentScriptHost = currentScriptHostURL.host +
 | |
|                     currentScriptHostURL.pathname.replace(/@vite\/client$/, '');
 | |
|                 console.error('[vite] failed to connect to websocket.\n' +
 | |
|                     'your current setup:\n' +
 | |
|                     `  (browser) ${currentScriptHost} <--[HTTP]--> ${serverHost} (server)\n` +
 | |
|                     `  (browser) ${socketHost} <--[WebSocket (failing)]--> ${directSocketHost} (server)\n` +
 | |
|                     'Check out your Vite / network configuration and https://vitejs.dev/config/server-options.html#server-hmr .');
 | |
|             });
 | |
|             socket.addEventListener('open', () => {
 | |
|                 console.info('[vite] Direct websocket connection fallback. Check out https://vitejs.dev/config/server-options.html#server-hmr to remove the previous connection error.');
 | |
|             }, { once: true });
 | |
|         };
 | |
|     }
 | |
|     socket = setupWebSocket(socketProtocol, socketHost, fallback);
 | |
| }
 | |
| catch (error) {
 | |
|     console.error(`[vite] failed to connect to websocket (${error}). `);
 | |
| }
 | |
| function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {
 | |
|     const socket = new WebSocket(`${protocol}://${hostAndPath}`, 'vite-hmr');
 | |
|     let isOpened = false;
 | |
|     socket.addEventListener('open', () => {
 | |
|         isOpened = true;
 | |
|     }, { once: true });
 | |
|     // Listen for messages
 | |
|     socket.addEventListener('message', async ({ data }) => {
 | |
|         handleMessage(JSON.parse(data));
 | |
|     });
 | |
|     // ping server
 | |
|     socket.addEventListener('close', async ({ wasClean }) => {
 | |
|         if (wasClean)
 | |
|             return;
 | |
|         if (!isOpened && onCloseWithoutOpen) {
 | |
|             onCloseWithoutOpen();
 | |
|             return;
 | |
|         }
 | |
|         console.log(`[vite] server connection lost. polling for restart...`);
 | |
|         await waitForSuccessfulPing(protocol, hostAndPath);
 | |
|         location.reload();
 | |
|     });
 | |
|     return socket;
 | |
| }
 | |
| function warnFailedFetch(err, path) {
 | |
|     if (!err.message.match('fetch')) {
 | |
|         console.error(err);
 | |
|     }
 | |
|     console.error(`[hmr] Failed to reload ${path}. ` +
 | |
|         `This could be due to syntax errors or importing non-existent ` +
 | |
|         `modules. (see errors above)`);
 | |
| }
 | |
| function cleanUrl(pathname) {
 | |
|     const url = new URL(pathname, location.toString());
 | |
|     url.searchParams.delete('direct');
 | |
|     return url.pathname + url.search;
 | |
| }
 | |
| let isFirstUpdate = true;
 | |
| const outdatedLinkTags = new WeakSet();
 | |
| async function handleMessage(payload) {
 | |
|     switch (payload.type) {
 | |
|         case 'connected':
 | |
|             console.debug(`[vite] connected.`);
 | |
|             sendMessageBuffer();
 | |
|             // proxy(nginx, docker) hmr ws maybe caused timeout,
 | |
|             // so send ping package let ws keep alive.
 | |
|             setInterval(() => {
 | |
|                 if (socket.readyState === socket.OPEN) {
 | |
|                     socket.send('{"type":"ping"}');
 | |
|                 }
 | |
|             }, __HMR_TIMEOUT__);
 | |
|             break;
 | |
|         case 'update':
 | |
|             notifyListeners('vite:beforeUpdate', payload);
 | |
|             // if this is the first update and there's already an error overlay, it
 | |
|             // means the page opened with existing server compile error and the whole
 | |
|             // module script failed to load (since one of the nested imports is 500).
 | |
|             // in this case a normal update won't work and a full reload is needed.
 | |
|             if (isFirstUpdate && hasErrorOverlay()) {
 | |
|                 window.location.reload();
 | |
|                 return;
 | |
|             }
 | |
|             else {
 | |
|                 clearErrorOverlay();
 | |
|                 isFirstUpdate = false;
 | |
|             }
 | |
|             await Promise.all(payload.updates.map(async (update) => {
 | |
|                 if (update.type === 'js-update') {
 | |
|                     return queueUpdate(fetchUpdate(update));
 | |
|                 }
 | |
|                 // css-update
 | |
|                 // this is only sent when a css file referenced with <link> is updated
 | |
|                 const { path, timestamp } = update;
 | |
|                 const searchUrl = cleanUrl(path);
 | |
|                 // can't use querySelector with `[href*=]` here since the link may be
 | |
|                 // using relative paths so we need to use link.href to grab the full
 | |
|                 // URL for the include check.
 | |
|                 const el = Array.from(document.querySelectorAll('link')).find((e) => !outdatedLinkTags.has(e) && cleanUrl(e.href).includes(searchUrl));
 | |
|                 if (!el) {
 | |
|                     return;
 | |
|                 }
 | |
|                 const newPath = `${base}${searchUrl.slice(1)}${searchUrl.includes('?') ? '&' : '?'}t=${timestamp}`;
 | |
|                 // rather than swapping the href on the existing tag, we will
 | |
|                 // create a new link tag. Once the new stylesheet has loaded we
 | |
|                 // will remove the existing link tag. This removes a Flash Of
 | |
|                 // Unstyled Content that can occur when swapping out the tag href
 | |
|                 // directly, as the new stylesheet has not yet been loaded.
 | |
|                 return new Promise((resolve) => {
 | |
|                     const newLinkTag = el.cloneNode();
 | |
|                     newLinkTag.href = new URL(newPath, el.href).href;
 | |
|                     const removeOldEl = () => {
 | |
|                         el.remove();
 | |
|                         console.debug(`[vite] css hot updated: ${searchUrl}`);
 | |
|                         resolve();
 | |
|                     };
 | |
|                     newLinkTag.addEventListener('load', removeOldEl);
 | |
|                     newLinkTag.addEventListener('error', removeOldEl);
 | |
|                     outdatedLinkTags.add(el);
 | |
|                     el.after(newLinkTag);
 | |
|                 });
 | |
|             }));
 | |
|             notifyListeners('vite:afterUpdate', payload);
 | |
|             break;
 | |
|         case 'custom': {
 | |
|             notifyListeners(payload.event, payload.data);
 | |
|             break;
 | |
|         }
 | |
|         case 'full-reload':
 | |
|             notifyListeners('vite:beforeFullReload', payload);
 | |
|             if (payload.path && payload.path.endsWith('.html')) {
 | |
|                 // if html file is edited, only reload the page if the browser is
 | |
|                 // currently on that page.
 | |
|                 const pagePath = decodeURI(location.pathname);
 | |
|                 const payloadPath = base + payload.path.slice(1);
 | |
|                 if (pagePath === payloadPath ||
 | |
|                     payload.path === '/index.html' ||
 | |
|                     (pagePath.endsWith('/') && pagePath + 'index.html' === payloadPath)) {
 | |
|                     location.reload();
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             else {
 | |
|                 location.reload();
 | |
|             }
 | |
|             break;
 | |
|         case 'prune':
 | |
|             notifyListeners('vite:beforePrune', payload);
 | |
|             // After an HMR update, some modules are no longer imported on the page
 | |
|             // but they may have left behind side effects that need to be cleaned up
 | |
|             // (.e.g style injections)
 | |
|             // TODO Trigger their dispose callbacks.
 | |
|             payload.paths.forEach((path) => {
 | |
|                 const fn = pruneMap.get(path);
 | |
|                 if (fn) {
 | |
|                     fn(dataMap.get(path));
 | |
|                 }
 | |
|             });
 | |
|             break;
 | |
|         case 'error': {
 | |
|             notifyListeners('vite:error', payload);
 | |
|             const err = payload.err;
 | |
|             if (enableOverlay) {
 | |
|                 createErrorOverlay(err);
 | |
|             }
 | |
|             else {
 | |
|                 console.error(`[vite] Internal Server Error\n${err.message}\n${err.stack}`);
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         default: {
 | |
|             const check = payload;
 | |
|             return check;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| function notifyListeners(event, data) {
 | |
|     const cbs = customListenersMap.get(event);
 | |
|     if (cbs) {
 | |
|         cbs.forEach((cb) => cb(data));
 | |
|     }
 | |
| }
 | |
| const enableOverlay = __HMR_ENABLE_OVERLAY__;
 | |
| function createErrorOverlay(err) {
 | |
|     if (!enableOverlay)
 | |
|         return;
 | |
|     clearErrorOverlay();
 | |
|     document.body.appendChild(new ErrorOverlay(err));
 | |
| }
 | |
| function clearErrorOverlay() {
 | |
|     document
 | |
|         .querySelectorAll(overlayId)
 | |
|         .forEach((n) => n.close());
 | |
| }
 | |
| function hasErrorOverlay() {
 | |
|     return document.querySelectorAll(overlayId).length;
 | |
| }
 | |
| let pending = false;
 | |
| let queued = [];
 | |
| /**
 | |
|  * buffer multiple hot updates triggered by the same src change
 | |
|  * so that they are invoked in the same order they were sent.
 | |
|  * (otherwise the order may be inconsistent because of the http request round trip)
 | |
|  */
 | |
| async function queueUpdate(p) {
 | |
|     queued.push(p);
 | |
|     if (!pending) {
 | |
|         pending = true;
 | |
|         await Promise.resolve();
 | |
|         pending = false;
 | |
|         const loading = [...queued];
 | |
|         queued = [];
 | |
|         (await Promise.all(loading)).forEach((fn) => fn && fn());
 | |
|     }
 | |
| }
 | |
| async function waitForSuccessfulPing(socketProtocol, hostAndPath, ms = 1000) {
 | |
|     const pingHostProtocol = socketProtocol === 'wss' ? 'https' : 'http';
 | |
|     // eslint-disable-next-line no-constant-condition
 | |
|     while (true) {
 | |
|         try {
 | |
|             // A fetch on a websocket URL will return a successful promise with status 400,
 | |
|             // but will reject a networking error.
 | |
|             // When running on middleware mode, it returns status 426, and an cors error happens if mode is not no-cors
 | |
|             await fetch(`${pingHostProtocol}://${hostAndPath}`, {
 | |
|                 mode: 'no-cors'
 | |
|             });
 | |
|             break;
 | |
|         }
 | |
|         catch (e) {
 | |
|             // wait ms before attempting to ping again
 | |
|             await new Promise((resolve) => setTimeout(resolve, ms));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| const sheetsMap = new Map();
 | |
| function updateStyle(id, content) {
 | |
|     let style = sheetsMap.get(id);
 | |
|     {
 | |
|         if (style && !(style instanceof HTMLStyleElement)) {
 | |
|             removeStyle(id);
 | |
|             style = undefined;
 | |
|         }
 | |
|         if (!style) {
 | |
|             style = document.createElement('style');
 | |
|             style.setAttribute('type', 'text/css');
 | |
|             style.setAttribute('data-vite-dev-id', id);
 | |
|             style.textContent = content;
 | |
|             document.head.appendChild(style);
 | |
|         }
 | |
|         else {
 | |
|             style.textContent = content;
 | |
|         }
 | |
|     }
 | |
|     sheetsMap.set(id, style);
 | |
| }
 | |
| function removeStyle(id) {
 | |
|     const style = sheetsMap.get(id);
 | |
|     if (style) {
 | |
|         if (style instanceof CSSStyleSheet) {
 | |
|             // @ts-expect-error: using experimental API
 | |
|             document.adoptedStyleSheets = document.adoptedStyleSheets.filter((s) => s !== style);
 | |
|         }
 | |
|         else {
 | |
|             document.head.removeChild(style);
 | |
|         }
 | |
|         sheetsMap.delete(id);
 | |
|     }
 | |
| }
 | |
| async function fetchUpdate({ path, acceptedPath, timestamp, explicitImportRequired }) {
 | |
|     const mod = hotModulesMap.get(path);
 | |
|     if (!mod) {
 | |
|         // In a code-splitting project,
 | |
|         // it is common that the hot-updating module is not loaded yet.
 | |
|         // https://github.com/vitejs/vite/issues/721
 | |
|         return;
 | |
|     }
 | |
|     const moduleMap = new Map();
 | |
|     const isSelfUpdate = path === acceptedPath;
 | |
|     // determine the qualified callbacks before we re-import the modules
 | |
|     const qualifiedCallbacks = mod.callbacks.filter(({ deps }) => deps.includes(acceptedPath));
 | |
|     if (isSelfUpdate || qualifiedCallbacks.length > 0) {
 | |
|         const dep = acceptedPath;
 | |
|         const disposer = disposeMap.get(dep);
 | |
|         if (disposer)
 | |
|             await disposer(dataMap.get(dep));
 | |
|         const [path, query] = dep.split(`?`);
 | |
|         try {
 | |
|             const newMod = await import(
 | |
|             /* @vite-ignore */
 | |
|             base +
 | |
|                 path.slice(1) +
 | |
|                 `?${explicitImportRequired ? 'import&' : ''}t=${timestamp}${query ? `&${query}` : ''}`);
 | |
|             moduleMap.set(dep, newMod);
 | |
|         }
 | |
|         catch (e) {
 | |
|             warnFailedFetch(e, dep);
 | |
|         }
 | |
|     }
 | |
|     return () => {
 | |
|         for (const { deps, fn } of qualifiedCallbacks) {
 | |
|             fn(deps.map((dep) => moduleMap.get(dep)));
 | |
|         }
 | |
|         const loggedPath = isSelfUpdate ? path : `${acceptedPath} via ${path}`;
 | |
|         console.debug(`[vite] hot updated: ${loggedPath}`);
 | |
|     };
 | |
| }
 | |
| function sendMessageBuffer() {
 | |
|     if (socket.readyState === 1) {
 | |
|         messageBuffer.forEach((msg) => socket.send(msg));
 | |
|         messageBuffer.length = 0;
 | |
|     }
 | |
| }
 | |
| const hotModulesMap = new Map();
 | |
| const disposeMap = new Map();
 | |
| const pruneMap = new Map();
 | |
| const dataMap = new Map();
 | |
| const customListenersMap = new Map();
 | |
| const ctxToListenersMap = new Map();
 | |
| function createHotContext(ownerPath) {
 | |
|     if (!dataMap.has(ownerPath)) {
 | |
|         dataMap.set(ownerPath, {});
 | |
|     }
 | |
|     // when a file is hot updated, a new context is created
 | |
|     // clear its stale callbacks
 | |
|     const mod = hotModulesMap.get(ownerPath);
 | |
|     if (mod) {
 | |
|         mod.callbacks = [];
 | |
|     }
 | |
|     // clear stale custom event listeners
 | |
|     const staleListeners = ctxToListenersMap.get(ownerPath);
 | |
|     if (staleListeners) {
 | |
|         for (const [event, staleFns] of staleListeners) {
 | |
|             const listeners = customListenersMap.get(event);
 | |
|             if (listeners) {
 | |
|                 customListenersMap.set(event, listeners.filter((l) => !staleFns.includes(l)));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     const newListeners = new Map();
 | |
|     ctxToListenersMap.set(ownerPath, newListeners);
 | |
|     function acceptDeps(deps, callback = () => { }) {
 | |
|         const mod = hotModulesMap.get(ownerPath) || {
 | |
|             id: ownerPath,
 | |
|             callbacks: []
 | |
|         };
 | |
|         mod.callbacks.push({
 | |
|             deps,
 | |
|             fn: callback
 | |
|         });
 | |
|         hotModulesMap.set(ownerPath, mod);
 | |
|     }
 | |
|     const hot = {
 | |
|         get data() {
 | |
|             return dataMap.get(ownerPath);
 | |
|         },
 | |
|         accept(deps, callback) {
 | |
|             if (typeof deps === 'function' || !deps) {
 | |
|                 // self-accept: hot.accept(() => {})
 | |
|                 acceptDeps([ownerPath], ([mod]) => deps && deps(mod));
 | |
|             }
 | |
|             else if (typeof deps === 'string') {
 | |
|                 // explicit deps
 | |
|                 acceptDeps([deps], ([mod]) => callback && callback(mod));
 | |
|             }
 | |
|             else if (Array.isArray(deps)) {
 | |
|                 acceptDeps(deps, callback);
 | |
|             }
 | |
|             else {
 | |
|                 throw new Error(`invalid hot.accept() usage.`);
 | |
|             }
 | |
|         },
 | |
|         // export names (first arg) are irrelevant on the client side, they're
 | |
|         // extracted in the server for propagation
 | |
|         acceptExports(_, callback) {
 | |
|             acceptDeps([ownerPath], callback && (([mod]) => callback(mod)));
 | |
|         },
 | |
|         dispose(cb) {
 | |
|             disposeMap.set(ownerPath, cb);
 | |
|         },
 | |
|         // @ts-expect-error untyped
 | |
|         prune(cb) {
 | |
|             pruneMap.set(ownerPath, cb);
 | |
|         },
 | |
|         // TODO
 | |
|         // eslint-disable-next-line @typescript-eslint/no-empty-function
 | |
|         decline() { },
 | |
|         // tell the server to re-perform hmr propagation from this module as root
 | |
|         invalidate() {
 | |
|             notifyListeners('vite:invalidate', { path: ownerPath });
 | |
|             this.send('vite:invalidate', { path: ownerPath });
 | |
|         },
 | |
|         // custom events
 | |
|         on(event, cb) {
 | |
|             const addToMap = (map) => {
 | |
|                 const existing = map.get(event) || [];
 | |
|                 existing.push(cb);
 | |
|                 map.set(event, existing);
 | |
|             };
 | |
|             addToMap(customListenersMap);
 | |
|             addToMap(newListeners);
 | |
|         },
 | |
|         send(event, data) {
 | |
|             messageBuffer.push(JSON.stringify({ type: 'custom', event, data }));
 | |
|             sendMessageBuffer();
 | |
|         }
 | |
|     };
 | |
|     return hot;
 | |
| }
 | |
| /**
 | |
|  * urls here are dynamic import() urls that couldn't be statically analyzed
 | |
|  */
 | |
| function injectQuery(url, queryToInject) {
 | |
|     // skip urls that won't be handled by vite
 | |
|     if (!url.startsWith('.') && !url.startsWith('/')) {
 | |
|         return url;
 | |
|     }
 | |
|     // can't use pathname from URL since it may be relative like ../
 | |
|     const pathname = url.replace(/#.*$/, '').replace(/\?.*$/, '');
 | |
|     const { search, hash } = new URL(url, 'http://vitejs.dev');
 | |
|     return `${pathname}?${queryToInject}${search ? `&` + search.slice(1) : ''}${hash || ''}`;
 | |
| }
 | |
| 
 | |
| export { ErrorOverlay, createHotContext, injectQuery, removeStyle, updateStyle };
 | |
| //# sourceMappingURL=client.mjs.map
 |