<!DOCTYPE html>
<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;
}
body {
background: #EEE;
color: #333;
}
textarea,
#output .outer {
background: #FFF;
border: 1px solid #CCC;
}
.terminal-1 {
font-weight: bold;
}
.terminal-37 {
color: #999;
}
.terminal-4 {
text-decoration: underline;
}
.terminal-31 {
color: #D00;
}
.terminal-32 {
color: #0D0;
}
.terminal-33 {
color: #CC0;
}
.terminal-35 {
color: #D0D;
}
@media (prefers-color-scheme: dark) {
body {
background: #333;
color: #DDD;
}
textarea,
#output .outer {
background: #111;
border: 1px solid #555;
}
.terminal-37 {
color: #777;
}
}
</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>ES2021</option>
<option selected>ESNext</option>
<option>Chrome70</option>
<option>Chrome71</option>
<option>Chrome72</option>
<option>Chrome73</option>
<option>Chrome74</option>
<option>Chrome75</option>
<option>Chrome76</option>
<option>Chrome77</option>
<option>Chrome78</option>
<option>Chrome79</option>
<option>Chrome80</option>
<option>Chrome81</option>
<option>Chrome82</option>
<option>Chrome83</option>
<option>Chrome84</option>
<option>Chrome85</option>
<option>Chrome86</option>
<option>Chrome87</option>
<option>Chrome88</option>
<option>Chrome89</option>
</select>
</label>
<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>
<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="bundle">
<input id="bundle" type="checkbox">
Bundle
</label>
<label for="ascii">
<input id="ascii" type="checkbox">
ASCII
</label>
<label for="keepNames">
<input id="keepNames" type="checkbox">
Keep Names
</label>
<label for="mangleProps">
<input id="mangleProps" type="checkbox">
Mangle Props
</label>
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 bundle = document.querySelector('#bundle')
const keepNames = document.querySelector('#keepNames')
const mangleProps = document.querySelector('#mangleProps')
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(bundle, 'bundle')
persistChecked(keepNames, 'keepNames')
persistChecked(mangleProps, 'mangleProps')
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 = async () => {
clearTimeout(debounceTimeout)
if (isRunning) {
needsRun = true
return
}
isRunning = true
needsRun = false
const inputValue = input.value
var code, warnings, errors
try {
if (bundle.checked) {
const results = await esbuild.build({
stdin: {
contents: inputValue,
loader: loader.value.toLowerCase(),
},
bundle: true,
target: target.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,
mangleProps: mangleProps.checked ? /_$/ : void 0,
plugins: [{
name: 'external-all',
setup(build) {
build.onResolve({ filter: /.*/ }, args => {
return { path: args.path, external: true }
})
},
}],
})
code = results.outputFiles[0].text
warnings = results.warnings
} else {
({ code, warnings } = await esbuild.transform(inputValue, {
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,
mangleProps: mangleProps.checked ? /_$/ : void 0,
}))
}
} catch (error) {
({ errors, warnings } = error)
if (!errors) {
output.textContent = (error && error.message) || (error + '')
isRunning = false
if (needsRun) runIfIdle()
return
}
}
if (warnings) warnings = await esbuild.formatMessages(warnings, { kind: 'warning', color: true })
if (errors) errors = await esbuild.formatMessages(errors, { kind: 'error', color: true })
const html = (errors || []).concat(warnings || []).map(messageToHTML).join('')
if (code) code = textToHTML(code)
if (code && minifySpaces.checked) code = `<span class="minified">${code}</span>`
output.innerHTML = (html && html + '<hr>' || '') + (code || '')
try {
sessionStorage.setItem('input', inputValue)
} catch (e) {
}
isRunning = false
if (needsRun) runIfIdle()
}
function textToHTML(text) {
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
}
function messageToHTML(text) {
let reset = ''
text = textToHTML(text)
text = text.replace(/\033\[([^m]+)m/g, (_, value) => {
if (value === '0') {
value = reset
reset = ''
} else {
reset += '</span>'
value = `<span class="terminal-${value}">`
}
return value
})
text += reset
return text
}
runIfIdle()
})
</script>
</body>
</html>