Add Tailwind CSS toggle, Markdown/WASM modes, and npm import resolution
- Tailwind CSS: toolbar checkbox injects Play CDN into preview, persisted per-fiddle via new options JSON column - Markdown mode: uses marked.js CDN, renders markdown to HTML preview with CSS tab for custom styling - WASM mode: starter template with inline WebAssembly add function, supports top-level await via module detection - npm imports: auto-detect bare import specifiers in module code and inject importmap pointing to esm.sh CDN - Module auto-detection for html-css-js mode (import/export statements) - DB migration adds options column, server passes through all API endpoints - All features work across preview, export, and embed
This commit is contained in:
@@ -3,6 +3,7 @@ import { loadScript } from './utils.js';
|
||||
let tsLoaded = false;
|
||||
let babelLoaded = false;
|
||||
let svelteLoaded = false;
|
||||
let markedLoaded = false;
|
||||
|
||||
async function ensureTypeScript() {
|
||||
if (tsLoaded) return;
|
||||
@@ -31,6 +32,19 @@ async function ensureBabel() {
|
||||
babelLoaded = true;
|
||||
}
|
||||
|
||||
async function ensureMarked() {
|
||||
if (markedLoaded) return;
|
||||
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/marked/15.0.7/marked.min.js');
|
||||
if (typeof marked === 'undefined') {
|
||||
await new Promise((resolve) => {
|
||||
const check = setInterval(() => {
|
||||
if (typeof marked !== 'undefined') { clearInterval(check); resolve(); }
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
markedLoaded = true;
|
||||
}
|
||||
|
||||
async function ensureSvelte() {
|
||||
if (svelteLoaded) return;
|
||||
await loadScript('https://unpkg.com/svelte@4.2.19/compiler.cjs');
|
||||
@@ -45,8 +59,10 @@ export async function compileJs(code, mode) {
|
||||
if (!code.trim()) return { js: '' };
|
||||
|
||||
switch (mode) {
|
||||
case 'html-css-js':
|
||||
return { js: code };
|
||||
case 'html-css-js': {
|
||||
const isModule = /(?:^|\n)\s*(?:import\s|export\s)/m.test(code);
|
||||
return { js: code, isModule };
|
||||
}
|
||||
|
||||
case 'typescript':
|
||||
return compileTypeScript(code);
|
||||
@@ -63,6 +79,16 @@ export async function compileJs(code, mode) {
|
||||
case 'svelte':
|
||||
return compileSvelte(code);
|
||||
|
||||
case 'markdown':
|
||||
return compileMarkdown(code);
|
||||
|
||||
case 'wasm': {
|
||||
// WASM starter uses top-level await, always treat as module
|
||||
const hasModuleSyntax = /(?:^|\n)\s*(?:import\s|export\s)/m.test(code);
|
||||
const hasTopLevelAwait = /(?:^|\n)\s*(?:const|let|var)\s+.*=\s*await\b/m.test(code) || /(?:^|\n)\s*await\s/m.test(code);
|
||||
return { js: code, isModule: hasModuleSyntax || hasTopLevelAwait };
|
||||
}
|
||||
|
||||
default:
|
||||
return { js: code };
|
||||
}
|
||||
@@ -125,6 +151,12 @@ function compileVue(code) {
|
||||
return { js, extraCss };
|
||||
}
|
||||
|
||||
async function compileMarkdown(code) {
|
||||
await ensureMarked();
|
||||
const renderedHtml = marked.parse(code);
|
||||
return { js: '', renderedHtml };
|
||||
}
|
||||
|
||||
async function compileSvelte(code) {
|
||||
await ensureSvelte();
|
||||
const result = svelte.compile(code, {
|
||||
@@ -185,6 +217,12 @@ export function getFrameworkRuntime(mode) {
|
||||
bodyHtml: '<div id="app"></div>',
|
||||
};
|
||||
|
||||
case 'markdown':
|
||||
return { scripts: [], bodyHtml: '' };
|
||||
|
||||
case 'wasm':
|
||||
return { scripts: [], bodyHtml: '' };
|
||||
|
||||
default:
|
||||
return { scripts: [], bodyHtml: '' };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user