Files
esbuild/scripts/try.html
2020-06-21 16:10:42 -07:00

284 lines
6.7 KiB
HTML

<!-- This is a simple playground to try out esbuild in your browser -->
<html>
<head>
<meta charset="utf8">
<title>Try esbuild live</title>
<style>
body {
font: 15px/120% sans-serif;
overflow: hidden;
}
h2,
p {
cursor: default;
user-select: none;
}
textarea {
resize: none;
width: 100%;
height: 100%;
}
textarea,
#output .outer {
box-sizing: border-box;
padding: 4px;
color: inherit;
font-size: 13px;
line-height: 110%;
font-family: monospace;
cursor: text;
}
#output .outer {
white-space: pre-wrap;
overflow-y: auto;
}
textarea:focus {
outline: none;
border: 1px solid #999;
}
hr {
border: 1px solid #999;
margin: 1em 0;
}
section {
position: absolute;
top: 0;
bottom: 0;
padding: 0 20px;
box-sizing: border-box;
}
#input {
left: 0;
width: 50%;
}
#output {
right: 0;
width: 50%;
}
.outer {
position: absolute;
left: 20px;
top: 100px;
right: 20px;
bottom: 20px;
}
#input .outer {
right: 10px;
}
#output .outer {
left: 10px;
}
label {
margin-right: 20px;
white-space: nowrap;
}
.terminal-error {
font-weight: bold;
color: #D00;
}
.terminal-underline {
font-weight: bold;
color: #0D0;
}
.terminal-warning {
font-weight: bold;
color: #D0D;
}
body {
background: #EEE;
color: #333;
}
textarea,
#output .outer {
background: #FFF;
border: 1px solid #CCC;
}
.terminal-subtle {
color: #777;
}
.terminal-bold {
font-weight: bold;
color: #000;
}
@media (prefers-color-scheme: dark) {
body {
background: #333;
color: #DDD;
}
textarea,
#output .outer {
background: #111;
border: 1px solid #555;
}
.terminal-subtle {
color: #999;
}
.terminal-bold {
color: #FFF;
}
}
</style>
</head>
<body>
<section id="input">
<h2>Input</h2>
<p>
<label for="target">
Target: <select id="target">
<option>ES2015</option>
<option>ES2016</option>
<option>ES2017</option>
<option>ES2018</option>
<option>ES2019</option>
<option>ES2020</option>
<option selected>ESNext</option>
</select>
</label>
<label for="loader">
Loader: <select id="loader">
<option selected>JS</option>
<option>JSX</option>
<option>TS</option>
<option>TSX</option>
</select>
</label>
<label for="minify">
<input id="minify" type="checkbox">
Minify
</label>
<label for="strict">
<input id="strict" type="checkbox">
Strict
</label>
</p>
<div class="outer"><textarea autofocus spellcheck="false"></textarea></div>
</section>
<section id="output">
<h2>Output</h2>
<div class="outer"></div>
</section>
<script src="../npm/esbuild-wasm/lib/browser.js"></script>
<script>
esbuild.startService({
wasmURL: '../npm/esbuild-wasm/esbuild.wasm',
}).then(service => {
const input = document.querySelector('#input textarea')
const output = document.querySelector('#output .outer')
const target = document.querySelector('#target')
const loader = document.querySelector('#loader')
const minify = document.querySelector('#minify')
const strict = document.querySelector('#strict')
let debounceTimeout = 0
let isRunning = false
let needsRun = false
target.onchange = runIfIdle
loader.onchange = runIfIdle
minify.onchange = runIfIdle
strict.onchange = runIfIdle
input.oninput = () => {
clearTimeout(debounceTimeout)
debounceTimeout = setTimeout(runIfIdle, 50)
}
function runIfIdle() {
clearTimeout(debounceTimeout)
if (isRunning) {
needsRun = true
return
}
isRunning = true
needsRun = false
service.transform(input.value, {
target: target.value.toLowerCase(),
loader: loader.value.toLowerCase(),
minify: minify.checked,
strict: strict.checked,
}).then(({ js, warnings }) => {
let html = messagesToHTML(addKindToMessages(warnings, 'warning'))
output.innerHTML = (html && html + '<hr>') + textToHTML(js);
}, ({ errors, warnings }) => {
output.innerHTML = messagesToHTML(addKindToMessages(errors, 'error').concat(addKindToMessages(warnings, 'warning')));
}).then(() => {
isRunning = false
if (needsRun) runIfIdle()
})
}
function textToHTML(text) {
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
function renderTabs(text) {
let result = ''
for (let c of text) {
if (c === '\t') result += ' '.repeat(8 - result.length % 8)
else result += c
}
return result
}
function addKindToMessages(messages, kind) {
return messages.map(({ text, location }) => ({ text, location, kind }))
}
function messagesToHTML(messages) {
return messages.sort((a, b) => {
let al = a.location, bl = b.location
return al && bl && (al.line - bl.line || al.column - bl.column) || (a.text < b.text) - (a.text > b.text)
}).map(({ text, location, kind }) => {
let html = ''
if (location) {
html = `<span class="terminal-bold">${textToHTML(location.file)}:${location.line}:${location.column}: </span>`
}
html += `<span class="terminal-${kind}">${kind}: </span>`
html += `<span class="terminal-bold">${textToHTML(text)}</span><br>`
if (location) {
let indent = renderTabs(location.lineText.slice(0, location.column)).length
let underline = location.length > 1 ? '~'.repeat(location.length) : '^'
let line = renderTabs(location.lineText)
html += `<span class="terminal-subtle">${textToHTML(line.slice(0, indent))}</span>`
html += `<span class="terminal-underline">${line.slice(indent, indent + location.length)}</span>`
html += `<span class="terminal-subtle">${textToHTML(line.slice(indent + location.length))}</span>`
html += `<br><span class="terminal-underline">${' '.repeat(indent)}${underline}</span><br>`
}
return html
}).join('');
}
})
</script>
</body>
</html>