123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- 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
|