Skip to content

Instantly share code, notes, and snippets.

@RyosukeCla
Last active February 14, 2021 18:53
Show Gist options
  • Save RyosukeCla/d184c8ad52b66c9f822bb0d71a7e4cbd to your computer and use it in GitHub Desktop.
Save RyosukeCla/d184c8ad52b66c9f822bb0d71a7e4cbd to your computer and use it in GitHub Desktop.
generate html from svelte
import * as svelte from 'svelte/compiler';
import * as rollup from 'rollup/dist/es/rollup.browser.js';
const PACKAGES_BASE_URL = 'https://unpkg.com';
export async function generateHtmlFromSvelteApp(svelteCode) {
const result = await bundleSvelteApp(svelteCode);
const code = result.output[0].code;
const generateHtml = Function(`
${code}
const fragment = document.createElement('div');
new SvelteComponent.default({
target: fragment,
});
const innerHTML = fragment.innerHTML;
fragment.remove();
return innerHTML;
`);
return generateHtml();
}
async function bundleSvelteApp(svelteCode) {
const lookups = [
{
resolve: "./App.svelte",
code: svelteCode,
}
];
const whilteList = [
"svelte/internal",
]
const bundle = await rollup.rollup({
input: './App.svelte',
plugins: [
sveltePlugin(lookups),
packagesPlugin(whilteList),
]
});
const bundleResult = await bundle.generate({
format: 'iife',
name: 'SvelteComponent',
exports: 'named',
sourcemap: true
});
await bundle.close();
return bundleResult;
}
function packagesPlugin(whilelist) {
const whitelistSet = new Set(whilelist);
return {
name: 'packagesPlugin',
async resolveId(source) {
if (!whitelistSet.has(source)) return null;
const packageJsonUrl = await followRedirects(`${PACKAGES_BASE_URL}/${source}/package.json`);
const packageJsonRes = await fetchIfUncached(packageJsonUrl);
const packageJson = JSON.parse(packageJsonRes.body);
if (packageJson.svelte || packageJson.module || packageJson.main) {
const url = packageJsonUrl.replace('/package.json', '');
return new URL(packageJson.svelte || packageJson.module || packageJson.main, `${url}/`).href;
}
return await followRedirects(`${PACKAGES_BASE_URL}/${source}`);
},
async load(resolveId) {
const res = await fetchIfUncached(resolveId);
return res.body;
},
}
}
/**
* @param {{ resolve: string, code: string }[] } lookups
*/
function sveltePlugin(lookups) {
const lookupMap = new Map();
lookups.forEach(lookup => {
lookupMap.set(lookup.resolve, lookup.code);
})
return {
name: 'lookupPlugin',
async resolveId(source) {
if (lookupMap.has(source)) return source;
return null;
},
async load(resolveId) {
if (lookupMap.has(resolveId)) return lookupMap.get(resolveId);
return null;
},
transform(code, resolveId) {
if (!/\.svelte$/.test(resolveId)) return null;
const result = svelte.compile(code, {
hydratable: true,
generate: 'dom',
format: 'esm',
css: false,
});
return result.js;
}
}
}
async function followRedirects(url) {
const res = await fetchIfUncached(url);
return res.url;
}
const fetch_cache = new Map();
function fetchIfUncached(url) {
if (fetch_cache.has(url)) {
return fetch_cache.get(url);
}
const promise = fetch(url)
.then(async r => {
if (r.ok) {
return {
url: r.url,
body: await r.text()
};
}
throw new Error(await r.text());
})
.catch(err => {
fetch_cache.delete(url);
throw err;
});
fetch_cache.set(url, promise);
return promise;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment