122 lines
4.0 KiB
JavaScript
122 lines
4.0 KiB
JavaScript
import { loadFiddle } from './api.js';
|
|
import { getFrameworkRuntime } from './js-preprocessors.js';
|
|
import { extractBareImports, buildImportMapTag } from './import-map.js';
|
|
|
|
const MODE_MAP = {
|
|
javascript: 'html-css-js',
|
|
typescript: 'typescript',
|
|
react: 'react',
|
|
'react-ts': 'react-ts',
|
|
vue: 'vue',
|
|
svelte: 'svelte',
|
|
markdown: 'markdown',
|
|
wasm: 'wasm',
|
|
};
|
|
|
|
async function init() {
|
|
// Extract fiddle ID from URL: /embed/:id
|
|
const match = location.pathname.match(/^\/embed\/([a-zA-Z0-9_-]+)$/);
|
|
if (!match) {
|
|
document.body.innerHTML = '<div class="embed-error">Invalid embed URL</div>';
|
|
return;
|
|
}
|
|
|
|
// URL params
|
|
const params = new URLSearchParams(location.search);
|
|
if (params.get('theme') === 'dark') document.body.classList.add('dark');
|
|
if (params.get('tabs') === '0') document.body.classList.add('no-tabs');
|
|
const autoRun = params.get('run') !== '0';
|
|
|
|
try {
|
|
const fiddle = await loadFiddle(match[1]);
|
|
const mode = MODE_MAP[fiddle.js_type] || 'html-css-js';
|
|
const runtime = getFrameworkRuntime(mode);
|
|
const opts = JSON.parse(fiddle.options || '{}');
|
|
|
|
// For embed, we compile client-side using the same preprocessors
|
|
const { compileCss } = await import('./preprocessors.js');
|
|
const { compileJs } = await import('./js-preprocessors.js');
|
|
|
|
const compiledCss = await compileCss(fiddle.css, fiddle.css_type || 'css');
|
|
const result = await compileJs(fiddle.js, mode);
|
|
|
|
const allCss = result.extraCss ? `${compiledCss}\n${result.extraCss}` : compiledCss;
|
|
|
|
const finalHtml = result.renderedHtml || fiddle.html;
|
|
const finalJs = result.renderedHtml ? '' : result.js;
|
|
|
|
let bodyContent;
|
|
if (mode === 'vue' || mode === 'svelte') {
|
|
bodyContent = finalHtml ? `${finalHtml}\n${runtime.bodyHtml}` : runtime.bodyHtml;
|
|
} else if (runtime.bodyHtml) {
|
|
bodyContent = `${finalHtml}\n${runtime.bodyHtml}`;
|
|
} else {
|
|
bodyContent = finalHtml;
|
|
}
|
|
|
|
const isModule = result.isModule || mode === 'svelte';
|
|
|
|
// Build importmap for module scripts with bare imports
|
|
let importMapTag = '';
|
|
if (isModule && finalJs) {
|
|
const bareImports = extractBareImports(finalJs);
|
|
if (bareImports.length) {
|
|
importMapTag = buildImportMapTag(bareImports);
|
|
}
|
|
}
|
|
|
|
let scripts = '';
|
|
if (finalJs) {
|
|
if (isModule) {
|
|
scripts = `<script type="module">\n${escapeScriptClose(finalJs)}\n<\/script>`;
|
|
} else {
|
|
for (const url of runtime.scripts) {
|
|
scripts += `<script src="${url}"><\/script>\n`;
|
|
}
|
|
scripts += `<script>\n${escapeScriptClose(finalJs)}\n<\/script>`;
|
|
}
|
|
}
|
|
|
|
const tailwindScript = opts.tailwind
|
|
? `<script>var _tw=console.warn;console.warn=function(){if(typeof arguments[0]==='string'&&arguments[0].indexOf('cdn.tailwindcss.com')!==-1)return;_tw.apply(console,arguments)}<\/script>\n<script src="https://cdn.tailwindcss.com"><\/script>\n<script>console.warn=_tw<\/script>\n`
|
|
: '';
|
|
|
|
// Dark preview theme — from fiddle options or URL param
|
|
const previewTheme = params.get('theme') || opts.previewTheme || 'light';
|
|
const darkCss = previewTheme === 'dark'
|
|
? `<style>body { background: #1e1e1e; color: #ccc; }</style>\n`
|
|
: '';
|
|
|
|
// External resources
|
|
let resourceTags = '';
|
|
const resources = opts.resources || [];
|
|
for (const r of resources) {
|
|
if (r.type === 'css') resourceTags += `<link rel="stylesheet" href="${r.url}">\n`;
|
|
else if (r.type === 'js') resourceTags += `<script src="${r.url}"><\/script>\n`;
|
|
}
|
|
|
|
const doc = `<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
${darkCss}${resourceTags}${tailwindScript}<style>${allCss}</style>
|
|
${importMapTag}
|
|
</head>
|
|
<body>
|
|
${bodyContent}
|
|
${scripts}
|
|
</body>
|
|
</html>`;
|
|
|
|
document.getElementById('preview-frame').srcdoc = doc;
|
|
} catch (e) {
|
|
document.body.innerHTML = `<div class="embed-error">Failed to load fiddle: ${e.message}</div>`;
|
|
}
|
|
}
|
|
|
|
function escapeScriptClose(code) {
|
|
return code.replace(/<\/script/gi, '<\\/script');
|
|
}
|
|
|
|
init();
|