Skip to content

Instantly share code, notes, and snippets.

@hyrious
Created January 20, 2024 09:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyrious/8a863ec2d78047af7e4a91689b2b0b3c to your computer and use it in GitHub Desktop.
Save hyrious/8a863ec2d78047af7e4a91689b2b0b3c to your computer and use it in GitHub Desktop.
esbuild # 3604
<textarea id="code" rows="15" cols="80">import {useState} from 'react'
import {Button} from '@mui/material'
export default function App() {
const [count, setCount] = useState(0)
return &lt;&gt;
&lt;Button variant="outlined" onClick={() => setCount(c => c + 1)}>
Count: {count}
&lt;/Button&gt;
&lt;/&gt;
}
</textarea>
<p><button onclick="run()">Run!</button></p>
<div id="target" style="border: 1px solid; padding: 1em; white-space: pre;">Press [Run].</div>
<script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
<script>
// Because 'react' and 'react-dom' should always use the same one,
// it is externalized from the server bundle and we inject them here.
window.require = (name) => {
if (name === 'react') return React
if (name === 'react-dom') return ReactDOM
throw new Error('Failed to require ' + JSON.stringify(name))
}
let root
function run() {
fetch('http://localhost:3000/build', {
method: 'POST',
body: document.getElementById('code').value
})
.then(async r => {
if (r.ok) return r.text()
throw new Error(await r.text())
})
.then(bundle => {
// bundle is a string like 'var serverBundle = (() => mod)()'
const mod = Function(bundle + '\nreturn serverBundle;')()
const App = mod && mod.__esModule ? mod.default : mod
root && root.unmount()
root = ReactDOM.createRoot(document.getElementById('target'))
root.render(React.createElement(App)) // root.render(<App />)
})
.catch(err => {
root && root.unmount()
root = null
document.getElementById('target').textContent = err + ''
})
}
</script>
{
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.15.5"
},
"devDependencies": {
"esbuild": "^0.19.11"
}
}
const http = require('node:http')
const esbuild = require('esbuild')
http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
if (req.method === 'POST' && req.url === '/build') {
return (async () => {
const chunks = []
for await (const c of req) chunks.push(c)
const code = Buffer.concat(chunks).toString('utf8')
const { outputFiles } = await esbuild.build({
stdin: { contents: code, loader: 'jsx', resolveDir: '.' },
bundle: true,
write: false,
globalName: 'serverBundle',
external: ['react', 'react-dom'], // will externalize 'react/jsx-runtime' too
plugins: [{
name: 'include-react-jsx-runtime',
setup({ onResolve }) {
onResolve({ filter: /^react\/jsx-runtime$/ }, args => {
return { path: require.resolve(args.path) }
})
}
}]
}).catch(err => {
res.statusCode = 400
res.end(String(err))
})
if (!outputFiles) return
const bundle = outputFiles[0].contents
res.end(bundle)
})()
}
res.statusCode = 404
res.end()
})
.listen(3000, () => console.log('POST http://localhost:3000/build "code" => "bundle"'))
@hyrious
Copy link
Author

hyrious commented Jan 20, 2024

  1. Download this package using the top-right corner "Download ZIP" button
  2. Extract it somewhere
  3. Run npm install in the package's folder
  4. Run node serve.js to start the backend service
  5. Open index.html in the browser, click the "Run!" button

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment