UNPKG

vite-plugin-react-server

Version:
126 lines (117 loc) 11.4 kB
/** * vite-plugin-react-server * Copyright (c) Nico Brinkkemper * MIT License */ const VIRTUAL_RSC_HMR = "virtual:react-server/hmr"; const RESOLVED_VIRTUAL_RSC_HMR = "\0" + VIRTUAL_RSC_HMR; const VIRTUAL_RSC_HMR_SOURCE = ( /* js */ ` import { useEffect, useCallback } from "react"; const RSC_HMR_EVENT = 'vite-plugin-react-server:server-component-update'; export { RSC_HMR_EVENT }; // Cache-bust every <link rel="stylesheet"> whose href references the changed // file. Server-collected CSS (vprs's <Css cssFiles={...}/> pattern) isn't in // the client module graph, so Vite's native CSS HMR never fires for these — // the <link> URL doesn't change and the browser keeps the cached stylesheet. // data.file is the project-relative path (eg "src/css/9mmc.module.css") and // the link href is a URL whose pathname ends with the same suffix. // We also fall back to basename matching to handle edge cases where the // project-relative form doesn't appear verbatim in the href. function refreshCssLinks(data) { if (!data) return false; const rel = String(data.file || '').replace(/^[\\\\/]+/, ''); if (!rel) return false; const basename = rel.split(/[\\\\/]/).pop(); const links = document.querySelectorAll('link[rel="stylesheet"]'); let refreshed = 0; for (const link of links) { const href = link.getAttribute('href'); if (!href) continue; let pathname; try { pathname = new URL(href, window.location.origin).pathname; } catch { pathname = href.split('?')[0]; } const matches = pathname.endsWith('/' + rel) || pathname.endsWith(rel) || (basename && pathname.endsWith('/' + basename)); if (!matches) continue; const url = new URL(href, window.location.origin); url.searchParams.set('t', String(Date.now())); link.setAttribute('href', url.pathname + url.search); refreshed++; } return refreshed > 0; } export function useRscHmr(refetch, options = {}) { const { verbose = true, filter } = options; const handler = useCallback( (data) => { if (filter && !filter(data)) return; if (verbose) { console.log('[RSC HMR] Server component updated:', data.file, data.kind ? '(' + data.kind + ')' : ''); } if (data && data.kind === 'css') { refreshCssLinks(data); } refetch(window.location.pathname); }, [refetch, verbose, filter] ); useEffect(() => { if (typeof import.meta.hot === 'undefined') return; import.meta.hot.on(RSC_HMR_EVENT, handler); if (verbose) { console.log('[RSC HMR] Listening for server component updates'); } return () => { import.meta.hot.off(RSC_HMR_EVENT, handler); }; }, [handler, verbose]); } export function setupRscHmr(options = {}) { const { onUpdate, verbose = true } = options; if (typeof import.meta.hot === 'undefined') return; import.meta.hot.on(RSC_HMR_EVENT, async (data) => { if (verbose) { console.log('[RSC HMR] Server component updated:', data.file, data.kind ? '(' + data.kind + ')' : ''); } if (data && data.kind === 'css') { refreshCssLinks(data); } if (onUpdate === 'reload') { window.location.reload(); return; } if (onUpdate) { try { await onUpdate(data); } catch (error) { console.error('[RSC HMR] Error in onUpdate handler:', error); window.location.reload(); } } else if (!data || data.kind !== 'css') { // For non-CSS updates without a handler, fall back to reload. // For CSS updates, refreshCssLinks already handled it visually. window.location.reload(); } }); if (verbose) { console.log('[RSC HMR] Listening for server component updates'); } } ` ); function virtualRscHmrPlugin() { return { name: "vite-plugin-react-server:virtual-rsc-hmr", resolveId(id) { if (id === VIRTUAL_RSC_HMR) return RESOLVED_VIRTUAL_RSC_HMR; }, load(id) { if (id === RESOLVED_VIRTUAL_RSC_HMR) return VIRTUAL_RSC_HMR_SOURCE; } }; } export { virtualRscHmrPlugin }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlydHVhbFJzY0htclBsdWdpbi5qcyIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcGx1Z2luL2Rldi1zZXJ2ZXIvdmlydHVhbFJzY0htclBsdWdpbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFBsdWdpbiB9IGZyb20gXCJ2aXRlXCI7XG5cbmNvbnN0IFZJUlRVQUxfUlNDX0hNUiA9ICd2aXJ0dWFsOnJlYWN0LXNlcnZlci9obXInO1xuY29uc3QgUkVTT0xWRURfVklSVFVBTF9SU0NfSE1SID0gJ1xcMCcgKyBWSVJUVUFMX1JTQ19ITVI7XG5cbi8qKlxuICogVmlydHVhbCBtb2R1bGUgc291cmNlIGZvciBgdmlydHVhbDpyZWFjdC1zZXJ2ZXIvaG1yYC5cbiAqIFNlbGYtY29udGFpbmVkIOKAlCBubyBpbXBvcnRzIGZyb20gdGhlIHBsdWdpbiBwYWNrYWdlLCBzbyBubyBkZXAgcmUtb3B0aW1pemF0aW9uLlxuICogSE1SIGNvZGUgaXMgZGVhZC1jb2RlIGVsaW1pbmF0ZWQgaW4gcHJvZHVjdGlvbiBidWlsZHMgKGltcG9ydC5tZXRhLmhvdCBpcyB1bmRlZmluZWQpLlxuICovXG5jb25zdCBWSVJUVUFMX1JTQ19ITVJfU09VUkNFID0gLyoganMgKi9gXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZUNhbGxiYWNrIH0gZnJvbSBcInJlYWN0XCI7XG5cbmNvbnN0IFJTQ19ITVJfRVZFTlQgPSAndml0ZS1wbHVnaW4tcmVhY3Qtc2VydmVyOnNlcnZlci1jb21wb25lbnQtdXBkYXRlJztcblxuZXhwb3J0IHsgUlNDX0hNUl9FVkVOVCB9O1xuXG4vLyBDYWNoZS1idXN0IGV2ZXJ5IDxsaW5rIHJlbD1cInN0eWxlc2hlZXRcIj4gd2hvc2UgaHJlZiByZWZlcmVuY2VzIHRoZSBjaGFuZ2VkXG4vLyBmaWxlLiBTZXJ2ZXItY29sbGVjdGVkIENTUyAodnBycydzIDxDc3MgY3NzRmlsZXM9ey4uLn0vPiBwYXR0ZXJuKSBpc24ndCBpblxuLy8gdGhlIGNsaWVudCBtb2R1bGUgZ3JhcGgsIHNvIFZpdGUncyBuYXRpdmUgQ1NTIEhNUiBuZXZlciBmaXJlcyBmb3IgdGhlc2Ug4oCUXG4vLyB0aGUgPGxpbms+IFVSTCBkb2Vzbid0IGNoYW5nZSBhbmQgdGhlIGJyb3dzZXIga2VlcHMgdGhlIGNhY2hlZCBzdHlsZXNoZWV0LlxuLy8gZGF0YS5maWxlIGlzIHRoZSBwcm9qZWN0LXJlbGF0aXZlIHBhdGggKGVnIFwic3JjL2Nzcy85bW1jLm1vZHVsZS5jc3NcIikgYW5kXG4vLyB0aGUgbGluayBocmVmIGlzIGEgVVJMIHdob3NlIHBhdGhuYW1lIGVuZHMgd2l0aCB0aGUgc2FtZSBzdWZmaXguXG4vLyBXZSBhbHNvIGZhbGwgYmFjayB0byBiYXNlbmFtZSBtYXRjaGluZyB0byBoYW5kbGUgZWRnZSBjYXNlcyB3aGVyZSB0aGVcbi8vIHByb2plY3QtcmVsYXRpdmUgZm9ybSBkb2Vzbid0IGFwcGVhciB2ZXJiYXRpbSBpbiB0aGUgaHJlZi5cbmZ1bmN0aW9uIHJlZnJlc2hDc3NMaW5rcyhkYXRhKSB7XG4gIGlmICghZGF0YSkgcmV0dXJuIGZhbHNlO1xuICBjb25zdCByZWwgPSBTdHJpbmcoZGF0YS5maWxlIHx8ICcnKS5yZXBsYWNlKC9eW1xcXFxcXFxcL10rLywgJycpO1xuICBpZiAoIXJlbCkgcmV0dXJuIGZhbHNlO1xuICBjb25zdCBiYXNlbmFtZSA9IHJlbC5zcGxpdCgvW1xcXFxcXFxcL10vKS5wb3AoKTtcbiAgY29uc3QgbGlua3MgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdsaW5rW3JlbD1cInN0eWxlc2hlZXRcIl0nKTtcbiAgbGV0IHJlZnJlc2hlZCA9IDA7XG4gIGZvciAoY29uc3QgbGluayBvZiBsaW5rcykge1xuICAgIGNvbnN0IGhyZWYgPSBsaW5rLmdldEF0dHJpYnV0ZSgnaHJlZicpO1xuICAgIGlmICghaHJlZikgY29udGludWU7XG4gICAgbGV0IHBhdGhuYW1lO1xuICAgIHRyeSB7XG4gICAgICBwYXRobmFtZSA9IG5ldyBVUkwoaHJlZiwgd2luZG93LmxvY2F0aW9uLm9yaWdpbikucGF0aG5hbWU7XG4gICAgfSBjYXRjaCB7XG4gICAgICBwYXRobmFtZSA9IGhyZWYuc3BsaXQoJz8nKVswXTtcbiAgICB9XG4gICAgY29uc3QgbWF0Y2hlcyA9XG4gICAgICBwYXRobmFtZS5lbmRzV2l0aCgnLycgKyByZWwpIHx8XG4gICAgICBwYXRobmFtZS5lbmRzV2l0aChyZWwpIHx8XG4gICAgICAoYmFzZW5hbWUgJiYgcGF0aG5hbWUuZW5kc1dpdGgoJy8nICsgYmFzZW5hbWUpKTtcbiAgICBpZiAoIW1hdGNoZXMpIGNvbnRpbnVlO1xuICAgIGNvbnN0IHVybCA9IG5ldyBVUkwoaHJlZiwgd2luZG93LmxvY2F0aW9uLm9yaWdpbik7XG4gICAgdXJsLnNlYXJjaFBhcmFtcy5zZXQoJ3QnLCBTdHJpbmcoRGF0ZS5ub3coKSkpO1xuICAgIGxpbmsuc2V0QXR0cmlidXRlKCdocmVmJywgdXJsLnBhdGhuYW1lICsgdXJsLnNlYXJjaCk7XG4gICAgcmVmcmVzaGVkKys7XG4gIH1cbiAgcmV0dXJuIHJlZnJlc2hlZCA+IDA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VSc2NIbXIocmVmZXRjaCwgb3B0aW9ucyA9IHt9KSB7XG4gIGNvbnN0IHsgdmVyYm9zZSA9IHRydWUsIGZpbHRlciB9ID0gb3B0aW9ucztcblxuICBjb25zdCBoYW5kbGVyID0gdXNlQ2FsbGJhY2soXG4gICAgKGRhdGEpID0+IHtcbiAgICAgIGlmIChmaWx0ZXIgJiYgIWZpbHRlcihkYXRhKSkgcmV0dXJuO1xuICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ1tSU0MgSE1SXSBTZXJ2ZXIgY29tcG9uZW50IHVwZGF0ZWQ6JywgZGF0YS5maWxlLCBkYXRhLmtpbmQgPyAnKCcgKyBkYXRhLmtpbmQgKyAnKScgOiAnJyk7XG4gICAgICB9XG4gICAgICBpZiAoZGF0YSAmJiBkYXRhLmtpbmQgPT09ICdjc3MnKSB7XG4gICAgICAgIHJlZnJlc2hDc3NMaW5rcyhkYXRhKTtcbiAgICAgIH1cbiAgICAgIHJlZmV0Y2god2luZG93LmxvY2F0aW9uLnBhdGhuYW1lKTtcbiAgICB9LFxuICAgIFtyZWZldGNoLCB2ZXJib3NlLCBmaWx0ZXJdXG4gICk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAodHlwZW9mIGltcG9ydC5tZXRhLmhvdCA9PT0gJ3VuZGVmaW5lZCcpIHJldHVybjtcbiAgICBpbXBvcnQubWV0YS5ob3Qub24oUlNDX0hNUl9FVkVOVCwgaGFuZGxlcik7XG4gICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgIGNvbnNvbGUubG9nKCdbUlNDIEhNUl0gTGlzdGVuaW5nIGZvciBzZXJ2ZXIgY29tcG9uZW50IHVwZGF0ZXMnKTtcbiAgICB9XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGltcG9ydC5tZXRhLmhvdC5vZmYoUlNDX0hNUl9FVkVOVCwgaGFuZGxlcik7XG4gICAgfTtcbiAgfSwgW2hhbmRsZXIsIHZlcmJvc2VdKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwUnNjSG1yKG9wdGlvbnMgPSB7fSkge1xuICBjb25zdCB7IG9uVXBkYXRlLCB2ZXJib3NlID0gdHJ1ZSB9ID0gb3B0aW9ucztcbiAgaWYgKHR5cGVvZiBpbXBvcnQubWV0YS5ob3QgPT09ICd1bmRlZmluZWQnKSByZXR1cm47XG4gIGltcG9ydC5tZXRhLmhvdC5vbihSU0NfSE1SX0VWRU5ULCBhc3luYyAoZGF0YSkgPT4ge1xuICAgIGlmICh2ZXJib3NlKSB7XG4gICAgICBjb25zb2xlLmxvZygnW1JTQyBITVJdIFNlcnZlciBjb21wb25lbnQgdXBkYXRlZDonLCBkYXRhLmZpbGUsIGRhdGEua2luZCA/ICcoJyArIGRhdGEua2luZCArICcpJyA6ICcnKTtcbiAgICB9XG4gICAgaWYgKGRhdGEgJiYgZGF0YS5raW5kID09PSAnY3NzJykge1xuICAgICAgcmVmcmVzaENzc0xpbmtzKGRhdGEpO1xuICAgIH1cbiAgICBpZiAob25VcGRhdGUgPT09ICdyZWxvYWQnKSB7XG4gICAgICB3aW5kb3cubG9jYXRpb24ucmVsb2FkKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChvblVwZGF0ZSkge1xuICAgICAgdHJ5IHsgYXdhaXQgb25VcGRhdGUoZGF0YSk7IH1cbiAgICAgIGNhdGNoIChlcnJvcikgeyBjb25zb2xlLmVycm9yKCdbUlNDIEhNUl0gRXJyb3IgaW4gb25VcGRhdGUgaGFuZGxlcjonLCBlcnJvcik7IHdpbmRvdy5sb2NhdGlvbi5yZWxvYWQoKTsgfVxuICAgIH0gZWxzZSBpZiAoIWRhdGEgfHwgZGF0YS5raW5kICE9PSAnY3NzJykge1xuICAgICAgLy8gRm9yIG5vbi1DU1MgdXBkYXRlcyB3aXRob3V0IGEgaGFuZGxlciwgZmFsbCBiYWNrIHRvIHJlbG9hZC5cbiAgICAgIC8vIEZvciBDU1MgdXBkYXRlcywgcmVmcmVzaENzc0xpbmtzIGFscmVhZHkgaGFuZGxlZCBpdCB2aXN1YWxseS5cbiAgICAgIHdpbmRvdy5sb2NhdGlvbi5yZWxvYWQoKTtcbiAgICB9XG4gIH0pO1xuICBpZiAodmVyYm9zZSkge1xuICAgIGNvbnNvbGUubG9nKCdbUlNDIEhNUl0gTGlzdGVuaW5nIGZvciBzZXJ2ZXIgY29tcG9uZW50IHVwZGF0ZXMnKTtcbiAgfVxufVxuYDtcblxuLyoqXG4gKiBQbHVnaW4gdGhhdCBwcm92aWRlcyB0aGUgYHZpcnR1YWw6cmVhY3Qtc2VydmVyL2htcmAgbW9kdWxlLlxuICogV29ya3MgaW4gYm90aCBkZXYgYW5kIGJ1aWxkIOKAlCBITVIgY29kZSB0cmVlLXNoYWtlcyBhd2F5IGluIHByb2R1Y3Rpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2aXJ0dWFsUnNjSG1yUGx1Z2luKCk6IFBsdWdpbiB7XG4gIHJldHVybiB7XG4gICAgbmFtZTogXCJ2aXRlLXBsdWdpbi1yZWFjdC1zZXJ2ZXI6dmlydHVhbC1yc2MtaG1yXCIsXG4gICAgcmVzb2x2ZUlkKGlkKSB7XG4gICAgICBpZiAoaWQgPT09IFZJUlRVQUxfUlNDX0hNUikgcmV0dXJuIFJFU09MVkVEX1ZJUlRVQUxfUlNDX0hNUjtcbiAgICB9LFxuICAgIGxvYWQoaWQpIHtcbiAgICAgIGlmIChpZCA9PT0gUkVTT0xWRURfVklSVFVBTF9SU0NfSE1SKSByZXR1cm4gVklSVFVBTF9SU0NfSE1SX1NPVVJDRTtcbiAgICB9LFxuICB9O1xufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBRUEsTUFBTSxlQUFrQixHQUFBLDBCQUFBO0FBQ3hCLE1BQU0sMkJBQTJCLElBQU8sR0FBQSxlQUFBO0FBT3hDLE1BQU0sc0JBQUE7QUFBQTtBQUFBLEVBQWlDO0FBQUE7O0FBQUE7O0FBQUE7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBOztBQUFBO0FBQUE7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLENBQUE7QUEwR2hDLFNBQVMsbUJBQThCLEdBQUE7QUFDckMsRUFBQSxPQUFBO0FBQUEsSUFDTCxJQUFNLEVBQUEsMENBQUE7QUFBQSxJQUNOLFVBQVUsRUFBSSxFQUFBO0FBQ1IsTUFBQSxJQUFBLEVBQUEsS0FBTyxpQkFBd0IsT0FBQSx3QkFBQTtBQUFBLEtBQ3JDO0FBQUEsSUFDQSxLQUFLLEVBQUksRUFBQTtBQUNILE1BQUEsSUFBQSxFQUFBLEtBQU8sMEJBQWlDLE9BQUEsc0JBQUE7QUFBQTtBQUM5QyxHQUNGO0FBQ0Y7Ozs7In0=