-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pure HTML Runtime Environment #1042
Comments
Some ideas from ChatGPT (o1-preview): Certainly! Let's update the old implementation to leverage modern browser APIs, focusing on using ES6 modules, Note: While service workers are a powerful tool for intercepting network requests, they cannot be registered from Blob URLs or data URLs due to security restrictions. Since we want to avoid calling out to a server and keep everything client-side, we'll use Blob URLs and override the Here's the updated code: export default function (template, files) {
// Collect the files, excluding 'index.html' and 'thumbnail.png'
const fileMap = {};
Object.keys(files).forEach((fileName) => {
if (fileName === 'index.html' || fileName === 'thumbnail.png') return;
fileMap[fileName] = files[fileName].content;
});
// Create Blob URLs for each file
const blobUrlMap = {};
Object.keys(fileMap).forEach((fileName) => {
const content = fileMap[fileName];
const blob = new Blob([content], { type: getContentType(fileName) });
const blobUrl = URL.createObjectURL(blob);
blobUrlMap[fileName] = blobUrl;
});
// Replace references in the template
template = replaceReferences(template, blobUrlMap);
// Inject code to override 'window.fetch' for fetch requests to the original filenames
const fetchOverrideScript = `
<script type="module">
(function() {
const originalFetch = window.fetch;
const blobUrlMap = ${JSON.stringify(blobUrlMap)};
window.fetch = function(input, init) {
const url = typeof input === 'string' ? input : input.url;
const pathname = new URL(url, location.href).pathname.slice(1);
if (blobUrlMap[pathname]) {
return originalFetch(blobUrlMap[pathname], init);
}
return originalFetch(input, init);
};
})();
</script>
`;
// Prepend the fetch override script to the template
template = fetchOverrideScript + template;
return template;
function getContentType(fileName) {
if (fileName.endsWith('.js')) {
return 'application/javascript';
} else if (fileName.endsWith('.css')) {
return 'text/css';
} else if (fileName.endsWith('.html')) {
return 'text/html';
} else if (fileName.endsWith('.json')) {
return 'application/json';
} else if (fileName.endsWith('.svg')) {
return 'image/svg+xml';
} else if (fileName.endsWith('.png')) {
return 'image/png';
} else if (fileName.endsWith('.jpg') || fileName.endsWith('.jpeg')) {
return 'image/jpeg';
} else if (fileName.endsWith('.gif')) {
return 'image/gif';
} else {
return 'text/plain';
}
}
function replaceReferences(html, blobUrlMap) {
// Replace <script src="file.js" type="module"> with Blob URL
html = html.replace(/<script\s+([^>]*?)src=["']([^"']+)["']([^>]*)><\/script>/g, (match, beforeSrc, src, afterSrc) => {
const fileName = src.startsWith('./') ? src.slice(2) : src;
if (blobUrlMap[fileName]) {
const attrs = `${beforeSrc.trim()} ${afterSrc.trim()}`.trim();
return `<script type="module" src="${blobUrlMap[fileName]}" ${attrs}></script>`;
}
return match;
});
// Replace <link href="file.css" rel="stylesheet"> with Blob URL
html = html.replace(/<link\s+([^>]*?)href=["']([^"']+)["']([^>]*)>/g, (match, beforeHref, href, afterHref) => {
const fileName = href.startsWith('./') ? href.slice(2) : href;
if (blobUrlMap[fileName]) {
const attrs = `${beforeHref.trim()} ${afterHref.trim()}`.trim();
return `<link href="${blobUrlMap[fileName]}" ${attrs}>`;
}
return match;
});
return html;
}
} Explanation:
Leveraging Modern Browser APIs:
Usage Example: Suppose you have the following const files = {
'index.html': {
content: `
<html>
<head>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Hello World</h1>
<script type="module" src="script.js"></script>
</body>
</html>
`,
},
'script.js': {
content: `
import { message } from './module.js';
console.log(message);
`,
},
'module.js': {
content: `
export const message = 'This is a module!';
`,
},
'styles.css': {
content: `
body { background-color: lightblue; }
`,
},
}; You can use the updated function as follows: const parseCode = /* the updated function */;
const modifiedTemplate = parseCode(files['index.html'].content, files);
// Now you can set the `modifiedTemplate` as the `srcdoc` of an iframe
const iframe = document.createElement('iframe');
iframe.srcdoc = modifiedTemplate;
document.body.appendChild(iframe); Considerations:
Benefits:
This updated implementation modernizes the original code by leveraging current web technologies, providing a seamless, in-browser coding experience that aligns with modern development practices. |
I think there is an opportunity to re-build the VizHub runtime environment to be based on purely HTML standards with ES Modules. In this variant of the runtime, there would be no need to run the source code through any various parsing and processing, meaning this version of the runtime would have zero dependency on Rollup.
Would indirectly solve by use of import maps and native ES modules the following issue:
Here's a start at it:
index.html
viz.js
index.js
This remains the same contract as the current V3 runtime environment - that
index.js
exports a function calledmain
that accepts a container, and the{ state, setState }
state management thing.This works great when locally hosted. I suppose this would have the downside of not supporting libraries that don't publish an ESM build.
In order to implement something like this as the next-gen VizHub runtime, I'd want to solve the following before launching it:
main
, which the current runtime v3 supports and is a really nice (dare I say "killer") feature to haveThe text was updated successfully, but these errors were encountered: