Files
esbuild/scripts/try.html
2020-07-05 10:50:55 -07:00

302 lines
7.5 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;
tab-size: 2;
}
#output .outer {
white-space: pre-wrap;
overflow-y: auto;
}
.minified {
word-break: break-all;
}
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 {
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>
&nbsp; &nbsp;
<label for="loader">
Loader: <select id="loader">
<option selected>JS</option>
<option>JSX</option>
<option>TS</option>
<option>TSX</option>
</select>
</label>
&nbsp; &nbsp;
<label for="strict">
<input id="strict" type="checkbox">
Strict
</label>
&nbsp; &nbsp;
Minify:
<label for="minify-syntax"><input id="minify-syntax" type="checkbox"> Syntax</label>
<label for="minify-idents"><input id="minify-idents" type="checkbox"> Identifiers</label>
<label for="minify-spaces"><input id="minify-spaces" type="checkbox"> Whitespace</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 minifySyntax = document.querySelector('#minify-syntax')
const minifyIdents = document.querySelector('#minify-idents')
const minifySpaces = document.querySelector('#minify-spaces')
const strict = document.querySelector('#strict')
let debounceTimeout = 0
let isRunning = false
let needsRun = false
target.onchange = runIfIdle
loader.onchange = runIfIdle
minifySyntax.onchange = runIfIdle
minifyIdents.onchange = runIfIdle
minifySpaces.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(),
minifySyntax: minifySyntax.checked,
minifyIdentifiers: minifyIdents.checked,
minifyWhitespace: minifySpaces.checked,
strict: strict.checked,
}).then(({ js, warnings }) => {
let html = messagesToHTML(addKindToMessages(warnings, 'warning'))
js = textToHTML(js);
if (minifySpaces.checked) js = `<span class="minified">${js}</span>`;
output.innerHTML = (html && html + '<hr>') + js;
}, error => {
let { errors, warnings } = error
if (errors && warnings) output.innerHTML = messagesToHTML(
addKindToMessages(errors, 'error').concat(addKindToMessages(warnings, 'warning')));
else output.textContent = (error && error.message) || (error + '')
}).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>