Files
esbuild/scripts/try.html
Evan Wallace caca86671d fix "try.html"
2021-03-08 20:51:30 -08:00

391 lines
9.9 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;
}
section p {
line-height: 26px;
}
#input {
left: 0;
width: 50%;
}
#output {
right: 0;
width: 50%;
}
.outer {
position: absolute;
left: 20px;
top: 120px;
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;
}
.terminal-note {
font-weight: bold;
}
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>ES5</option>
<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>
<option>CSS</option>
<option>JSON</option>
<option>Text</option>
<option>Base64</option>
<option>DataURL</option>
<option>Binary</option>
</select>
</label>
&nbsp; &nbsp;
<label for="format">
Format: <select id="format">
<option selected>Preserve</option>
<option>IIFE</option>
<option>CJS</option>
<option>ESM</option>
</select>
</label>
<br>
<label for="ascii">
<input id="ascii" type="checkbox">
ASCII
</label>
&nbsp; &nbsp;
<label for="keepNames">
<input id="keepNames" type="checkbox">
Keep Names
</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>
const input = document.querySelector('#input textarea')
const target = document.querySelector('#target')
const loader = document.querySelector('#loader')
const format = document.querySelector('#format')
const minifySyntax = document.querySelector('#minify-syntax')
const minifyIdents = document.querySelector('#minify-idents')
const minifySpaces = document.querySelector('#minify-spaces')
const ascii = document.querySelector('#ascii')
const keepNames = document.querySelector('#keepNames')
let runIfIdle
function persistValue(el, key) {
el.onchange = () => {
runIfIdle()
try {
sessionStorage.setItem(key, el.value)
} catch (e) {
}
}
try {
const old = sessionStorage.getItem(key)
if (old !== null) el.value = old
} catch (e) {
}
}
function persistChecked(el, key) {
el.onchange = () => {
runIfIdle()
try {
sessionStorage.setItem(key, el.checked)
} catch (e) {
}
}
try {
const old = sessionStorage.getItem(key)
if (old !== null) el.checked = old === 'true'
} catch (e) {
}
}
persistValue(target, 'target')
persistValue(loader, 'loader')
persistValue(format, 'format')
persistChecked(minifySyntax, 'minifySyntax')
persistChecked(minifyIdents, 'minifyIdents')
persistChecked(minifySpaces, 'minifySpaces')
persistChecked(ascii, 'ascii')
persistChecked(keepNames, 'keepNames')
try {
const old = sessionStorage.getItem('input')
if (old !== null) input.value = old
} catch (e) {
}
esbuild.initialize({
wasmURL: '../npm/esbuild-wasm/esbuild.wasm',
}).then(() => {
const output = document.querySelector('#output .outer')
let debounceTimeout = 0
let isRunning = false
let needsRun = false
input.oninput = () => {
clearTimeout(debounceTimeout)
debounceTimeout = setTimeout(runIfIdle, 50)
}
runIfIdle = () => {
clearTimeout(debounceTimeout)
if (isRunning) {
needsRun = true
return
}
isRunning = true
needsRun = false
esbuild.transform(input.value, {
target: target.value.toLowerCase(),
loader: loader.value.toLowerCase(),
format: format.value === 'Preserve' ? void 0 : format.value.toLowerCase(),
minifySyntax: minifySyntax.checked,
minifyIdentifiers: minifyIdents.checked,
minifyWhitespace: minifySpaces.checked,
charset: ascii.checked ? 'ascii' : 'utf8',
keepNames: keepNames.checked,
}).then(({ code, warnings }) => {
let html = messagesToHTML(addKindToMessages(warnings, 'warning'))
code = textToHTML(code);
if (minifySpaces.checked) code = `<span class="minified">${code}</span>`;
output.innerHTML = (html && html + '<hr>') + code;
}, 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()
})
try {
sessionStorage.setItem('input', input.value)
} catch (e) {
}
}
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(msg => ({ ...msg, kind }))
}
function renderMessage(indent, text, location, kind) {
let spaces = ' '.repeat(indent.length + 2)
let html = ''
if (kind !== 'note') html += `<span class="terminal-bold">`
html += indent
if (location) {
html += `${textToHTML(location.file)}:${location.line}:${location.column}: `
}
html += `<span class="terminal-${kind}">${kind}: </span>`
html += textToHTML(text)
if (kind !== 'note') html += `</span>`
html += `<br>`
if (location) {
let indent = renderTabs(location.lineText.slice(0, location.column)).length
let length = renderTabs(location.lineText.slice(0, location.column + location.length)).length - indent
let underline = length > 1 ? '~'.repeat(length) : '^'
let line = renderTabs(location.lineText)
html += `${spaces}<span class="terminal-subtle">${textToHTML(line.slice(0, indent))}</span>`
html += `<span class="terminal-underline">${line.slice(indent, indent + length)}</span>`
html += `<span class="terminal-subtle">${textToHTML(line.slice(indent + length))}</span>`
html += `<br>${spaces}<span class="terminal-underline">${' '.repeat(indent)}${underline}</span><br>`
}
return html
}
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, notes }) => {
return renderMessage(' &gt; ', text, location, kind) + notes.map(({ text, location }) => renderMessage(' ', text, location, 'note')) + `<br>`
}).join('');
}
runIfIdle()
})
</script>
</body>
</html>