@tomjs/vscode-extension-webview
Version:
When using 'webview.html' in vscode extension development, 'HMR' of vue/react is supported.
197 lines (173 loc) • 5.44 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/template.html
var template_default = `
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#webview-patch-iframe {
width: 100%;
height: 100%;
border: none;
}
.outer {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<script type="module" id="webview-patch">
const TAG = '[@tomjs:vscode:extension] ';
function onDomReady(callback, doc) {
const _doc = doc || document;
if (_doc.readyState === 'interactive' || _doc.readyState === 'complete') {
callback();
} else {
_doc.addEventListener('DOMContentLoaded', callback);
}
}
let vsCodeApi;
function getApi() {
if (vsCodeApi) return vsCodeApi;
return (vsCodeApi = acquireVsCodeApi());
}
function sendInitData(iframe) {
console.log(TAG + 'init data');
const dataset = {};
Object.keys(document.body.dataset).forEach(key => {
dataset[key] = document.body.dataset[key];
});
iframe.contentWindow.postMessage(
{
type: '[vscode:extension]:init',
data: {
state: getApi().getState(),
style: document.getElementById('_defaultStyles').innerHTML,
root: {
cssText: document.documentElement.style.cssText,
},
body: {
dataset: dataset,
className: document.body.className,
role: document.body.getAttribute('role'),
},
},
},
'*',
);
}
function observeAttributeChanges(element, attributeName, callback) {
const observer = new MutationObserver(function (mutationsList) {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === attributeName) {
callback(mutation.target.getAttribute(attributeName));
}
}
});
observer.observe(element, { attributes: true });
return observer;
}
// message handler
let iframeLoaded = false;
const cacheMessages = [];
function handleMessage(e) {
const iframe = document.getElementById('webview-patch-iframe');
if (!iframeLoaded || !iframe) {
return;
}
if (e.origin.startsWith('vscode-webview://')) {
iframe.contentWindow.postMessage(e.data, '*');
} else if ('{{serverUrl}}'.startsWith(e.origin)) {
const { type, data } = e.data;
console.log(TAG + ' received:', e.data);
if (type === '[vscode:client]:postMessage') {
getApi().postMessage(data);
}
}
}
window.addEventListener('message', function (event) {
if (event.origin.startsWith('vscode-webview://')) {
cacheMessages.push(event);
return;
}
handleMessage(event);
});
let isCacheWorking = false;
setInterval(() => {
if (isCacheWorking) {
return;
}
isCacheWorking = true;
if (iframeLoaded) {
let event = cacheMessages.shift();
while (event) {
handleMessage(event);
event = cacheMessages.shift();
}
}
isCacheWorking = false;
}, 50);
onDomReady(function () {
/** @type {HTMLIFrameElement} */
const iframe = document.getElementById('webview-patch-iframe');
observeAttributeChanges(document.body, 'class', function (className) {
sendInitData(iframe);
});
onDomReady(function () {
iframeLoaded = true;
sendInitData(iframe);
}, iframe.contentDocument);
iframe.addEventListener('load', function (e) {
iframeLoaded = true;
let interval = setInterval(() => {
try {
if (document.getElementById('_defaultStyles')) {
sendInitData(iframe);
clearInterval(interval);
return;
}
} catch (e) {
clearInterval(interval);
console.error(e);
}
}, 10);
});
});
</script>
</head>
<body>
<div class="outer">
<iframe
id="webview-patch-iframe"
frameborder="0"
sandbox="allow-scripts allow-same-origin allow-forms allow-pointer-lock allow-downloads"
allow="cross-origin-isolated; autoplay; clipboard-read; clipboard-write"
src="{{serverUrl}}"
></iframe>
</div>
</body>
</html>
`;
// src/index.ts
function getHtml(options) {
const opts = {
serverUrl: ""
};
if (typeof options === "string") {
opts.serverUrl = options;
} else {
Object.assign(opts, options);
}
return template_default.replace(new RegExp("{{serverUrl}}", "g"), opts.serverUrl);
}
var src_default = getHtml;
exports.default = src_default; exports.getHtml = getHtml;