fix: rewrite inline preview modules

The preview proxy now rewrites imports inside inline module scripts, which covers the Vite React preamble emitted into HTML. This keeps /@react-refresh behind the same proxy path and preserves preview/url auth tokens for iframe subresources. Added coverage so future proxy rewrites do not regress Vite React preview startup.
This commit is contained in:
Bohdan Triapitsyn
2026-06-03 18:19:28 +03:00
parent 833e166589
commit 297663c2d7
2 changed files with 46 additions and 16 deletions
@@ -1041,21 +1041,6 @@ export const rewritePreviewBody = ({ bodyText, proxyBasePath, targetOrigin, kind
}
return value;
};
const rewriteHtml = (text) => text
.replace(/\b(src|href|action)=(['"])([^'"]*)\2/gi, (_match, attr, quote, value) => {
return `${attr}=${quote}${rewriteResourceUrl(value)}${quote}`;
})
.replace(/\bsrcset=(['"])([^'"]*)\1/gi, (_match, quote, value) => {
const rewritten = String(value).split(',').map((part) => {
const trimmed = part.trim();
if (!trimmed) return trimmed;
const segments = trimmed.split(/\s+/);
const url = segments[0] || '';
segments[0] = rewriteResourceUrl(url);
return segments.join(' ');
}).join(', ');
return `srcset=${quote}${rewritten}${quote}`;
});
const stripPreviewCspMeta = (text) => text
.replace(/<meta\b(?=[^>]*\bhttp-equiv\s*=\s*(['"])content-security-policy\1)[^>]*>/gi, '')
.replace(/<meta\b(?=[^>]*\bhttp-equiv\s*=\s*content-security-policy\b)[^>]*>/gi, '');
@@ -1077,6 +1062,35 @@ export const rewritePreviewBody = ({ bodyText, proxyBasePath, targetOrigin, kind
.replace(/\bimport\(\s*(['"])\/(?!\/)([^'"]*)\1\s*\)/gi, (_match, quote, path) => {
return `import(${quote}${rewriteResourceUrl(`/${path}`)}${quote})`;
});
const rewriteInlineModuleScripts = (text) => text.replace(
/<script\b([^>]*)>([\s\S]*?)<\/script>/gi,
(match, attrs, scriptBody) => {
if (/\bsrc\s*=/i.test(attrs)) return match;
const typeMatch = String(attrs || '').match(/\btype\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i);
const type = String(typeMatch?.[1] ?? typeMatch?.[2] ?? typeMatch?.[3] ?? '').trim().toLowerCase();
if (type !== 'module') return match;
const rewrittenScriptBody = rewriteJavaScript(scriptBody);
if (rewrittenScriptBody === scriptBody) return match;
return `<script${attrs}>${rewrittenScriptBody}</script>`;
},
);
const rewriteHtml = (text) => rewriteInlineModuleScripts(text
.replace(/\b(src|href|action)=(['"])([^'"]*)\2/gi, (_match, attr, quote, value) => {
return `${attr}=${quote}${rewriteResourceUrl(value)}${quote}`;
})
.replace(/\bsrcset=(['"])([^'"]*)\1/gi, (_match, quote, value) => {
const rewritten = String(value).split(',').map((part) => {
const trimmed = part.trim();
if (!trimmed) return trimmed;
const segments = trimmed.split(/\s+/);
const url = segments[0] || '';
segments[0] = rewriteResourceUrl(url);
return segments.join(' ');
}).join(', ');
return `srcset=${quote}${rewritten}${quote}`;
}));
if (kind === 'html') return stripPreviewCspMeta(rewriteHtml(bodyText));
if (kind === 'css') return rewriteCss(bodyText);